mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 15:16:29 +00:00
Compare commits
284 Commits
REL5_0_STA
...
v5.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fb3432398 | ||
|
|
5e59e543d6 | ||
|
|
4f6b642320 | ||
|
|
c1973438f7 | ||
|
|
fe5904e04e | ||
|
|
2cb1f4f728 | ||
|
|
5b90842c55 | ||
|
|
397e0ed5be | ||
|
|
8f3994b071 | ||
|
|
f7c232b393 | ||
|
|
ac2feba380 | ||
|
|
725e9f9851 | ||
|
|
00bf4d61fa | ||
|
|
338c4d3f8a | ||
|
|
250c6291df | ||
|
|
773159a9e8 | ||
|
|
5f986bc981 | ||
|
|
d62743ddf4 | ||
|
|
b195547525 | ||
|
|
758c18985c | ||
|
|
7969dc4800 | ||
|
|
0fc8c6c79c | ||
|
|
e7acb6809b | ||
|
|
4b524c52b6 | ||
|
|
b3b9281253 | ||
|
|
679cfe0852 | ||
|
|
e10d9fd393 | ||
|
|
467d19bcd4 | ||
|
|
be244e2155 | ||
|
|
42283bf344 | ||
|
|
5b254a1be9 | ||
|
|
9aaf7d79a2 | ||
|
|
e86c035242 | ||
|
|
73d2088a85 | ||
|
|
48f95f9a39 | ||
|
|
ce229beff8 | ||
|
|
16eeae700c | ||
|
|
70061c51aa | ||
|
|
3ffeffbd8b | ||
|
|
147f454d32 | ||
|
|
26b5664741 | ||
|
|
cb86180f4f | ||
|
|
1f3e098104 | ||
|
|
4670515285 | ||
|
|
bccc2673b6 | ||
|
|
158008c5c5 | ||
|
|
5f3d1cdeb6 | ||
|
|
82515a9733 | ||
|
|
73e8373337 | ||
|
|
f1bdb09512 | ||
|
|
028e3ab48d | ||
|
|
b5b7d635ad | ||
|
|
146654bf0e | ||
|
|
4c3aed2573 | ||
|
|
3945314e65 | ||
|
|
f4938a4a42 | ||
|
|
9a836b3c04 | ||
|
|
c8e52e486f | ||
|
|
1f7ac843fd | ||
|
|
8d57d7e001 | ||
|
|
13e7c679cd | ||
|
|
466590af28 | ||
|
|
a88c80248c | ||
|
|
1131e3aad2 | ||
|
|
0630d9644e | ||
|
|
c50a2d049c | ||
|
|
20100f5aaa | ||
|
|
2d20c110bf | ||
|
|
aed0045c3a | ||
|
|
cd81046c26 | ||
|
|
fb32284cdc | ||
|
|
893044f1e9 | ||
|
|
271d407c7c | ||
|
|
1d0103ca44 | ||
|
|
51d56684a7 | ||
|
|
4d88f177a7 | ||
|
|
de3f0802b4 | ||
|
|
2985b9d91f | ||
|
|
300e11eb76 | ||
|
|
53546b1c88 | ||
|
|
547bbb06d8 | ||
|
|
0d0ffc675c | ||
|
|
11dc923a20 | ||
|
|
e97319f01d | ||
|
|
db1cb1433f | ||
|
|
c1428a3ecd | ||
|
|
fc568a9101 | ||
|
|
e65738c989 | ||
|
|
aaea24b58b | ||
|
|
d75a35a788 | ||
|
|
8233560629 | ||
|
|
cf60844c45 | ||
|
|
a0d3fae7ab | ||
|
|
f05978e2e1 | ||
|
|
b7475792e7 | ||
|
|
5c16e94672 | ||
|
|
ff4771ab02 | ||
|
|
d59cadd5f6 | ||
|
|
04aee7b406 | ||
|
|
029164a817 | ||
|
|
3dde8f1386 | ||
|
|
94fbf76b2e | ||
|
|
4a1855fabe | ||
|
|
d79d4c50b2 | ||
|
|
2071fa8c7e | ||
|
|
9945a3a4a8 | ||
|
|
0df0db1281 | ||
|
|
682ec9184a | ||
|
|
fdc6f61257 | ||
|
|
f5018e42f3 | ||
|
|
26689871dc | ||
|
|
a863dc7f6c | ||
|
|
9b6fe6858a | ||
|
|
2f667116d8 | ||
|
|
8ee4fac5bb | ||
|
|
bb56387aaa | ||
|
|
5d00094936 | ||
|
|
ebdfdc530d | ||
|
|
e5d3285d02 | ||
|
|
fd52df0fab | ||
|
|
1b5ad743b5 | ||
|
|
389c0ab9c0 | ||
|
|
72dfe28e81 | ||
|
|
bc566f7a42 | ||
|
|
d1ab6ce28b | ||
|
|
bcc284cac9 | ||
|
|
d37513312a | ||
|
|
3ca642fee1 | ||
|
|
be8e5b45fa | ||
|
|
5ee4540640 | ||
|
|
6507a374f7 | ||
|
|
c884f21a58 | ||
|
|
11e1b7a2c5 | ||
|
|
d0c5dffe91 | ||
|
|
38b3447bd3 | ||
|
|
3200c8c4e4 | ||
|
|
971309c830 | ||
|
|
1628bfb846 | ||
|
|
8adcb1348d | ||
|
|
025e66ea46 | ||
|
|
4e48301d78 | ||
|
|
f8b214f721 | ||
|
|
96acd3f915 | ||
|
|
c1584d587c | ||
|
|
2f26a02b5c | ||
|
|
cb2fb53556 | ||
|
|
e1953742a1 | ||
|
|
97d83bd443 | ||
|
|
45e96f21a5 | ||
|
|
9df1731fb8 | ||
|
|
cfd35852b7 | ||
|
|
32dde4eaaf | ||
|
|
78f89a4d47 | ||
|
|
410dd40526 | ||
|
|
3d85d8f5ff | ||
|
|
d20711c267 | ||
|
|
e4a7da0132 | ||
|
|
4f0e8a503a | ||
|
|
c39289d570 | ||
|
|
662b603770 | ||
|
|
fa4f37ddb9 | ||
|
|
599bab590a | ||
|
|
cd80f265ac | ||
|
|
09f0be8ceb | ||
|
|
59159dede7 | ||
|
|
780453e168 | ||
|
|
8a27c89d18 | ||
|
|
0a2091d5d3 | ||
|
|
447054a630 | ||
|
|
d998cab3d0 | ||
|
|
7f460c88bf | ||
|
|
e59da2d74e | ||
|
|
bffb8fa11b | ||
|
|
d9cb38c7f0 | ||
|
|
f3258c5002 | ||
|
|
5d92c99bb9 | ||
|
|
6895916914 | ||
|
|
e64349e4da | ||
|
|
2e9bc31c8c | ||
|
|
325e3ea541 | ||
|
|
2b06f2d1ae | ||
|
|
304c1391cc | ||
|
|
b09631b3bc | ||
|
|
e561ddc8d3 | ||
|
|
06f0e5e94f | ||
|
|
12adb5e0d1 | ||
|
|
b691a1bd10 | ||
|
|
dd35c22033 | ||
|
|
ddde31b14e | ||
|
|
09a78111f6 | ||
|
|
76cea52755 | ||
|
|
57ba3ef19a | ||
|
|
0ddc9b8bbf | ||
|
|
5722c0a582 | ||
|
|
eb346ac6ae | ||
|
|
0bc0a28378 | ||
|
|
fb5ce720f3 | ||
|
|
7c96afc6fb | ||
|
|
6559258b53 | ||
|
|
9de31428f1 | ||
|
|
10304a1a3b | ||
|
|
63aac64938 | ||
|
|
8f6058c676 | ||
|
|
194b6d0948 | ||
|
|
c6dfe53f03 | ||
|
|
e218422eca | ||
|
|
e02e3dae29 | ||
|
|
6ef722956b | ||
|
|
cebb1249aa | ||
|
|
51a7c31833 | ||
|
|
76af2d9e08 | ||
|
|
3b03edebb6 | ||
|
|
eaee7145f6 | ||
|
|
2bb89d252b | ||
|
|
e782f2d949 | ||
|
|
5d058dc371 | ||
|
|
089b3ecb8b | ||
|
|
b4af80fdec | ||
|
|
cb7bbda021 | ||
|
|
9cf4616af1 | ||
|
|
6f01c54620 | ||
|
|
7ed0a99d70 | ||
|
|
e2a362a171 | ||
|
|
cd7f36a6fd | ||
|
|
ab9c84c655 | ||
|
|
0141bc2be7 | ||
|
|
4d6cff6c42 | ||
|
|
84b824d86a | ||
|
|
a7689ecd78 | ||
|
|
ef30892250 | ||
|
|
3ae6691d34 | ||
|
|
bd8eb82fb9 | ||
|
|
4d4ed3bcd6 | ||
|
|
7fdf2f1778 | ||
|
|
46222cc0ae | ||
|
|
afa88f0514 | ||
|
|
647c0c879e | ||
|
|
3f5d2f6ee9 | ||
|
|
ab6e5ceab3 | ||
|
|
eb1d5c4e93 | ||
|
|
b3c09c48bf | ||
|
|
e2ffeac67d | ||
|
|
4ed72eb901 | ||
|
|
f158e35c13 | ||
|
|
21475b9c70 | ||
|
|
95ee576052 | ||
|
|
ac753c2ba1 | ||
|
|
ce85ba6df5 | ||
|
|
c3aba173ea | ||
|
|
93acdcfda2 | ||
|
|
25fb24eee4 | ||
|
|
220ec7fc96 | ||
|
|
1a9bcddccd | ||
|
|
f0693271d3 | ||
|
|
c23162e787 | ||
|
|
b8f323af5a | ||
|
|
63217e436a | ||
|
|
45b9002e5b | ||
|
|
52f9cd3bae | ||
|
|
cc540a54e5 | ||
|
|
9083f26990 | ||
|
|
5405ae7100 | ||
|
|
aa2674e284 | ||
|
|
b007d5ed4b | ||
|
|
63ddc2d39e | ||
|
|
9976e646cd | ||
|
|
dc11330d58 | ||
|
|
be494f0d5f | ||
|
|
d7fd55be99 | ||
|
|
0574279ccb | ||
|
|
b74f965f54 | ||
|
|
b9e360d5b8 | ||
|
|
047249e980 | ||
|
|
14f46b076e | ||
|
|
52abe309df | ||
|
|
f45b9d7024 | ||
|
|
de67fa2441 | ||
|
|
5f6d970fd9 | ||
|
|
0dce03a5f8 | ||
|
|
5d81e03d2d | ||
|
|
fdb61a1dea | ||
|
|
8a38188c47 | ||
|
|
5dcca6b053 | ||
|
|
a0591afb1e |
@@ -2,7 +2,7 @@ License and Contributions
|
||||
=========================
|
||||
|
||||
`repmgr` is licensed under the GPL v3. All of its code and documentation is
|
||||
Copyright 2010-2019, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
|
||||
Copyright 2010-2020, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
|
||||
details.
|
||||
|
||||
The development of repmgr has primarily been sponsored by 2ndQuadrant customers.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2010-2019, 2ndQuadrant Limited
|
||||
Copyright (c) 2010-2020, 2ndQuadrant Limited
|
||||
All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
54
HISTORY
54
HISTORY
@@ -1,6 +1,54 @@
|
||||
5.0.1 20??-??-??
|
||||
repmgr: ensure an existing replication slot is not deleted if the
|
||||
follow target is the node's current upstream (Ian)
|
||||
5.2.0 2020-10-22
|
||||
general: add support for PostgreSQL 13 (Ian)
|
||||
general: remove support for PostgreSQL 9.3 (Ian)
|
||||
config: add support for file inclusion directives (Ian)
|
||||
repmgr: "primary unregister --force" will unregister an active primary
|
||||
with no registered standby nodes (Ian)
|
||||
repmgr: add option --verify-backup to "standby clone" (Ian)
|
||||
repmgr: "standby clone" honours --waldir option if set in
|
||||
"pg_basebackup_options" (Ian)
|
||||
repmgr: add option --db-connection to "node check" (Ian)
|
||||
repmgr: report database connection error if the --optformat option was
|
||||
provided to "node check" (Ian)
|
||||
repmgr: improve "node rejoin" checks (Ian)
|
||||
repmgr: enable "node rejoin" to join a target with a lower timeline (Ian)
|
||||
repmgr: support pg_rewind's automatic crash recovery in Pg13 and later (Ian)
|
||||
repmgr: improve output formatting for cluster matrix/crosscheck (Ian)
|
||||
repmgr: improve database connection failure error checking on the
|
||||
demotion candidate during "standby switchover" (Ian)
|
||||
repmgr: make repmgr metadata tables dumpable (Ian)
|
||||
repmgr: fix issue with tablespace mapping when cloning from Barman;
|
||||
GitHub #650 (Ian)
|
||||
repmgr: improve handling of pg_control read errors (Ian)
|
||||
repmgrd: add additional optional parameters to "failover_validation command"
|
||||
(spaskalev; GitHub #651)
|
||||
repmgrd: ensure primary connection is reset if same as upstream;
|
||||
GitHub #633 (Ian)
|
||||
|
||||
5.1.0 2020-04-13
|
||||
repmgr: remove BDR 2.x support
|
||||
repmgr: don't query upstream's data directory (Ian)
|
||||
repmgr: rename --recovery-conf-only to --replication-conf-only (Ian)
|
||||
repmgr: ensure postgresql.auto.conf is created with correct permissions (Ian)
|
||||
repmgr: minimize requirement to check upstream data directory location
|
||||
during "standby clone" (Ian)
|
||||
repmgr: warn about missing pg_rewind prerequisites when excuting
|
||||
"standby clone" (Ian)
|
||||
repmgr: add --upstream option to "node check"
|
||||
repmgr: report error code on follow/rejoin failure due to non-available
|
||||
0 replication slot (Ian)
|
||||
repmgr: ensure "node rejoin" checks for available replication slots (Ian)
|
||||
repmgr: improve "standby switchover" completion checks (Ian)
|
||||
repmgr: add replication configuration file ownership check to
|
||||
"standby switchover" (Ian)
|
||||
repmgr: check the demotion candidate's registered repmgr.conf file can
|
||||
be found (laixiong; GitHub 615)
|
||||
repmgr: consolidate replication connection code (Ian)
|
||||
repmgr: check permissions for "pg_promote()" and fall back to pg_ctl
|
||||
if necessary (Ian)
|
||||
repmgr: in --dry-run mode, display promote command which will be used (Ian)
|
||||
repmgr: enable "service_promote_command" in PostgreSQL 12 (Ian)
|
||||
repmgr: accept option -S/--superuser for "node check"; GitHub #612 (Ian)
|
||||
|
||||
5.0 2019-10-15
|
||||
general: add PostgreSQL 12 support (Ian)
|
||||
|
||||
17
Makefile.in
17
Makefile.in
@@ -11,6 +11,8 @@ EXTENSION = repmgr
|
||||
|
||||
DATA = \
|
||||
repmgr--unpackaged--4.0.sql \
|
||||
repmgr--unpackaged--5.1.sql \
|
||||
repmgr--unpackaged--5.2.sql \
|
||||
repmgr--4.0.sql \
|
||||
repmgr--4.0--4.1.sql \
|
||||
repmgr--4.1.sql \
|
||||
@@ -21,7 +23,11 @@ DATA = \
|
||||
repmgr--4.3--4.4.sql \
|
||||
repmgr--4.4.sql \
|
||||
repmgr--4.4--5.0.sql \
|
||||
repmgr--5.0.sql
|
||||
repmgr--5.0.sql \
|
||||
repmgr--5.0--5.1.sql \
|
||||
repmgr--5.1.sql \
|
||||
repmgr--5.1--5.2.sql \
|
||||
repmgr--5.2.sql
|
||||
|
||||
REGRESS = repmgr_extension
|
||||
|
||||
@@ -53,9 +59,12 @@ $(info Building against PostgreSQL $(MAJORVERSION))
|
||||
|
||||
REPMGR_CLIENT_OBJS = repmgr-client.o \
|
||||
repmgr-action-primary.o repmgr-action-standby.o repmgr-action-witness.o \
|
||||
repmgr-action-bdr.o repmgr-action-cluster.o repmgr-action-node.o repmgr-action-service.o repmgr-action-daemon.o \
|
||||
configfile.o configfile-scan.o log.o strutil.o controldata.o dirutil.o compat.o dbutils.o sysutils.o
|
||||
REPMGRD_OBJS = repmgrd.o repmgrd-physical.o repmgrd-bdr.o configfile.o configfile-scan.o log.o dbutils.o strutil.o controldata.o compat.o sysutils.o
|
||||
repmgr-action-cluster.o repmgr-action-node.o repmgr-action-service.o repmgr-action-daemon.o \
|
||||
configdata.o configfile.o configfile-scan.o log.o strutil.o controldata.o dirutil.o compat.o \
|
||||
dbutils.o sysutils.o
|
||||
REPMGRD_OBJS = repmgrd.o repmgrd-physical.o configdata.o configfile.o configfile-scan.o log.o \
|
||||
dbutils.o strutil.o controldata.o compat.o sysutils.o
|
||||
|
||||
DATE=$(shell date "+%Y-%m-%d")
|
||||
|
||||
repmgr_version.h: repmgr_version.h.in
|
||||
|
||||
2
compat.c
2
compat.c
@@ -6,7 +6,7 @@
|
||||
* supported PostgreSQL versions. They're unlikely to change but
|
||||
* it would be worth keeping an eye on them for any fixes/improvements.
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
|
||||
2
compat.h
2
compat.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* compat.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
|
||||
936
configdata.c
Normal file
936
configdata.c
Normal file
@@ -0,0 +1,936 @@
|
||||
/*
|
||||
* configdata.c - contains structs with parsed configuration data
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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 "repmgr.h"
|
||||
#include "configfile.h"
|
||||
|
||||
/*
|
||||
* Parsed configuration settings are stored here
|
||||
*/
|
||||
t_configuration_options config_file_options;
|
||||
|
||||
|
||||
/*
|
||||
* Configuration settings are defined here
|
||||
*/
|
||||
|
||||
struct ConfigFileSetting config_file_settings[] =
|
||||
{
|
||||
|
||||
/* ================
|
||||
* node information
|
||||
* ================
|
||||
*/
|
||||
/* node_id */
|
||||
{
|
||||
"node_id",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.node_id },
|
||||
{ .intdefault = UNKNOWN_NODE_ID },
|
||||
{ .intminval = MIN_NODE_ID },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* node_name */
|
||||
{
|
||||
"node_name",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.node_name },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.node_name) },
|
||||
{}
|
||||
},
|
||||
/* conninfo */
|
||||
{
|
||||
"conninfo",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.conninfo },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.conninfo) },
|
||||
{}
|
||||
},
|
||||
/* replication_user */
|
||||
{
|
||||
"replication_user",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.replication_user },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.replication_user) },
|
||||
{}
|
||||
},
|
||||
/* data_directory */
|
||||
{
|
||||
"data_directory",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.data_directory },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.data_directory) },
|
||||
{ .postprocess_func = &repmgr_canonicalize_path }
|
||||
},
|
||||
/* config_directory */
|
||||
{
|
||||
"config_directory",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.config_directory },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.config_directory) },
|
||||
{ .postprocess_func = &repmgr_canonicalize_path }
|
||||
},
|
||||
/* pg_bindir */
|
||||
{
|
||||
"pg_bindir",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_bindir },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_bindir) },
|
||||
{ .postprocess_func = &repmgr_canonicalize_path }
|
||||
},
|
||||
/* repmgr_bindir */
|
||||
{
|
||||
"repmgr_bindir",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.repmgr_bindir },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.repmgr_bindir) },
|
||||
{ .postprocess_func = &repmgr_canonicalize_path }
|
||||
},
|
||||
/* replication_type */
|
||||
{
|
||||
"replication_type",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.replication_type },
|
||||
{ .intdefault = REPLICATION_TYPE_PHYSICAL },
|
||||
{ .intminval = -1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* ================
|
||||
* logging settings
|
||||
* ================
|
||||
*/
|
||||
|
||||
/*
|
||||
* log_level
|
||||
* NOTE: the default for "log_level" is set in log.c and does not need
|
||||
* to be initialised here
|
||||
*/
|
||||
{
|
||||
"log_level",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.log_level },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.log_level) },
|
||||
{}
|
||||
},
|
||||
/* log_facility */
|
||||
{
|
||||
"log_facility",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.log_facility },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.log_facility) },
|
||||
{}
|
||||
},
|
||||
/* log_file */
|
||||
{
|
||||
"log_file",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.log_file },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.log_file) },
|
||||
{ .postprocess_func = &repmgr_canonicalize_path }
|
||||
},
|
||||
/* log_status_interval */
|
||||
{
|
||||
"log_status_interval",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.log_status_interval },
|
||||
{ .intdefault = DEFAULT_LOG_STATUS_INTERVAL, },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* ======================
|
||||
* standby clone settings
|
||||
* ======================
|
||||
*/
|
||||
/* use_replication_slots */
|
||||
{
|
||||
"use_replication_slots",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.use_replication_slots },
|
||||
{ .booldefault = DEFAULT_USE_REPLICATION_SLOTS },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* pg_basebackup_options */
|
||||
{
|
||||
"pg_basebackup_options",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_basebackup_options },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_basebackup_options) },
|
||||
{}
|
||||
},
|
||||
/* restore_command */
|
||||
{
|
||||
"restore_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.restore_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.restore_command) },
|
||||
{}
|
||||
},
|
||||
/* tablespace_mapping */
|
||||
{
|
||||
"tablespace_mapping",
|
||||
CONFIG_TABLESPACE_MAPPING,
|
||||
{ .tablespacemappingptr = &config_file_options.tablespace_mapping },
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* recovery_min_apply_delay */
|
||||
{
|
||||
"recovery_min_apply_delay",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.recovery_min_apply_delay },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.recovery_min_apply_delay) },
|
||||
{
|
||||
.process_func = &parse_time_unit_parameter,
|
||||
.providedptr = &config_file_options.recovery_min_apply_delay_provided
|
||||
}
|
||||
},
|
||||
|
||||
/* archive_cleanup_command */
|
||||
{
|
||||
"archive_cleanup_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.archive_cleanup_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.archive_cleanup_command) },
|
||||
{}
|
||||
},
|
||||
|
||||
/* use_primary_conninfo_password */
|
||||
{
|
||||
"use_primary_conninfo_password",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.use_primary_conninfo_password },
|
||||
{ .booldefault = DEFAULT_USE_PRIMARY_CONNINFO_PASSWORD },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* passfile */
|
||||
{
|
||||
"passfile",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.passfile },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.passfile) },
|
||||
{}
|
||||
},
|
||||
|
||||
/* ======================
|
||||
* standby clone settings
|
||||
* ======================
|
||||
*/
|
||||
/* promote_check_timeout */
|
||||
{
|
||||
"promote_check_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.promote_check_timeout },
|
||||
{ .intdefault = DEFAULT_PROMOTE_CHECK_TIMEOUT },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* promote_check_interval */
|
||||
{
|
||||
"promote_check_interval",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.promote_check_interval },
|
||||
{ .intdefault = DEFAULT_PROMOTE_CHECK_INTERVAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* =======================
|
||||
* standby follow settings
|
||||
* =======================
|
||||
*/
|
||||
/* primary_follow_timeout */
|
||||
{
|
||||
"primary_follow_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.primary_follow_timeout },
|
||||
{ .intdefault = DEFAULT_PRIMARY_FOLLOW_TIMEOUT, },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* standby_follow_timeout */
|
||||
{
|
||||
"standby_follow_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.standby_follow_timeout },
|
||||
{ .intdefault = DEFAULT_STANDBY_FOLLOW_TIMEOUT },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* standby_follow_restart */
|
||||
{
|
||||
"standby_follow_restart",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.standby_follow_restart },
|
||||
{ .booldefault = DEFAULT_STANDBY_FOLLOW_RESTART },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* ===========================
|
||||
* standby switchover settings
|
||||
* ===========================
|
||||
*/
|
||||
/* shutdown_check_timeout */
|
||||
{
|
||||
"shutdown_check_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.shutdown_check_timeout },
|
||||
{ .intdefault = DEFAULT_SHUTDOWN_CHECK_TIMEOUT },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* standby_reconnect_timeout */
|
||||
{
|
||||
"standby_reconnect_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.standby_reconnect_timeout },
|
||||
{ .intdefault = DEFAULT_STANDBY_RECONNECT_TIMEOUT },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* wal_receive_check_timeout */
|
||||
{
|
||||
"wal_receive_check_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.wal_receive_check_timeout },
|
||||
{ .intdefault = DEFAULT_WAL_RECEIVE_CHECK_TIMEOUT },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* ====================
|
||||
* node rejoin settings
|
||||
* ====================
|
||||
*/
|
||||
/* node_rejoin_timeout */
|
||||
{
|
||||
"node_rejoin_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.node_rejoin_timeout },
|
||||
{ .intdefault = DEFAULT_NODE_REJOIN_TIMEOUT },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* ===================
|
||||
* node check settings
|
||||
* ===================
|
||||
*/
|
||||
/* archive_ready_warning */
|
||||
{
|
||||
"archive_ready_warning",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.archive_ready_warning },
|
||||
{ .intdefault = DEFAULT_ARCHIVE_READY_WARNING },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* archive_ready_critical */
|
||||
{
|
||||
"archive_ready_critical",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.archive_ready_critical },
|
||||
{ .intdefault = DEFAULT_ARCHIVE_READY_CRITICAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* replication_lag_warning */
|
||||
{
|
||||
"replication_lag_warning",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.replication_lag_warning },
|
||||
{ .intdefault = DEFAULT_REPLICATION_LAG_WARNING },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* replication_lag_critical */
|
||||
{
|
||||
"replication_lag_critical",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.replication_lag_critical },
|
||||
{ .intdefault = DEFAULT_REPLICATION_LAG_CRITICAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* ================
|
||||
* witness settings
|
||||
* ================
|
||||
*/
|
||||
/* witness_sync_interval */
|
||||
{
|
||||
"witness_sync_interval",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.witness_sync_interval },
|
||||
{ .intdefault = DEFAULT_WITNESS_SYNC_INTERVAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* ================
|
||||
* repmgrd settings
|
||||
* ================
|
||||
*/
|
||||
/* failover */
|
||||
{
|
||||
"failover",
|
||||
CONFIG_FAILOVER_MODE,
|
||||
{ .failovermodeptr = &config_file_options.failover },
|
||||
{ .failovermodedefault = FAILOVER_MANUAL },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* location */
|
||||
{
|
||||
"location",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.location },
|
||||
{ .strdefault = DEFAULT_LOCATION },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.location) },
|
||||
{}
|
||||
},
|
||||
/* priority */
|
||||
{
|
||||
"priority",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.priority },
|
||||
{ .intdefault = DEFAULT_PRIORITY, },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* promote_command */
|
||||
{
|
||||
"promote_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.promote_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.promote_command) },
|
||||
{}
|
||||
},
|
||||
/* follow_command */
|
||||
{
|
||||
"follow_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.follow_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.follow_command) },
|
||||
{}
|
||||
},
|
||||
/* monitor_interval_secs */
|
||||
{
|
||||
"monitor_interval_secs",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.monitor_interval_secs },
|
||||
{ .intdefault = DEFAULT_MONITORING_INTERVAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* reconnect_attempts */
|
||||
{
|
||||
"reconnect_attempts",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.reconnect_attempts },
|
||||
{ .intdefault = DEFAULT_RECONNECTION_ATTEMPTS },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* reconnect_interval */
|
||||
{
|
||||
"reconnect_interval",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.reconnect_interval },
|
||||
{ .intdefault = DEFAULT_RECONNECTION_INTERVAL },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
|
||||
/* monitoring_history */
|
||||
{
|
||||
"monitoring_history",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.monitoring_history },
|
||||
{ .booldefault = DEFAULT_MONITORING_HISTORY },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* degraded_monitoring_timeout */
|
||||
{
|
||||
"degraded_monitoring_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.degraded_monitoring_timeout },
|
||||
{ .intdefault = DEFAULT_DEGRADED_MONITORING_TIMEOUT },
|
||||
{ .intminval = -1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* async_query_timeout */
|
||||
{
|
||||
"async_query_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.async_query_timeout },
|
||||
{ .intdefault = DEFAULT_ASYNC_QUERY_TIMEOUT },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* primary_notification_timeout */
|
||||
{
|
||||
"primary_notification_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.primary_notification_timeout },
|
||||
{ .intdefault = DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* repmgrd_standby_startup_timeout */
|
||||
{
|
||||
"repmgrd_standby_startup_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.repmgrd_standby_startup_timeout },
|
||||
{ .intdefault = DEFAULT_REPMGRD_STANDBY_STARTUP_TIMEOUT },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* repmgrd_pid_file */
|
||||
{
|
||||
"repmgrd_pid_file",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.repmgrd_pid_file },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.repmgrd_pid_file) },
|
||||
{ .postprocess_func = &repmgr_canonicalize_path }
|
||||
},
|
||||
/* standby_disconnect_on_failover */
|
||||
{
|
||||
"standby_disconnect_on_failover",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.standby_disconnect_on_failover },
|
||||
{ .booldefault = DEFAULT_STANDBY_DISCONNECT_ON_FAILOVER },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* sibling_nodes_disconnect_timeout */
|
||||
{
|
||||
"sibling_nodes_disconnect_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.sibling_nodes_disconnect_timeout },
|
||||
{ .intdefault = DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* connection_check_type */
|
||||
{
|
||||
"connection_check_type",
|
||||
CONFIG_CONNECTION_CHECK_TYPE,
|
||||
{ .checktypeptr = &config_file_options.connection_check_type },
|
||||
{ .checktypedefault = DEFAULT_CONNECTION_CHECK_TYPE },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* primary_visibility_consensus */
|
||||
{
|
||||
"primary_visibility_consensus",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.primary_visibility_consensus },
|
||||
{ .booldefault = DEFAULT_PRIMARY_VISIBILITY_CONSENSUS },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* always_promote */
|
||||
{
|
||||
"always_promote",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.always_promote },
|
||||
{ .booldefault = DEFAULT_ALWAYS_PROMOTE },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* failover_validation_command */
|
||||
{
|
||||
"failover_validation_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.failover_validation_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.failover_validation_command) },
|
||||
{}
|
||||
},
|
||||
/* election_rerun_interval */
|
||||
{
|
||||
"election_rerun_interval",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.election_rerun_interval },
|
||||
{ .intdefault = DEFAULT_ELECTION_RERUN_INTERVAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* child_nodes_check_interval */
|
||||
{
|
||||
"child_nodes_check_interval",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.child_nodes_check_interval },
|
||||
{ .intdefault = DEFAULT_CHILD_NODES_CHECK_INTERVAL },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* child_nodes_disconnect_min_count */
|
||||
{
|
||||
"child_nodes_disconnect_min_count",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.child_nodes_disconnect_min_count },
|
||||
{ .intdefault = DEFAULT_CHILD_NODES_DISCONNECT_MIN_COUNT },
|
||||
{ .intminval = -1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* child_nodes_connected_min_count */
|
||||
{
|
||||
"child_nodes_connected_min_count",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.child_nodes_connected_min_count },
|
||||
{ .intdefault = DEFAULT_CHILD_NODES_CONNECTED_MIN_COUNT},
|
||||
{ .intminval = -1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* child_nodes_connected_include_witness */
|
||||
{
|
||||
"child_nodes_connected_include_witness",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.child_nodes_connected_include_witness },
|
||||
{ .booldefault = DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* child_nodes_disconnect_timeout */
|
||||
{
|
||||
"child_nodes_disconnect_timeout",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.child_nodes_disconnect_timeout },
|
||||
{ .intdefault = DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT },
|
||||
{ .intminval = 0 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* child_nodes_disconnect_command */
|
||||
{
|
||||
"child_nodes_disconnect_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.child_nodes_disconnect_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.child_nodes_disconnect_command) },
|
||||
{}
|
||||
},
|
||||
/* ================
|
||||
* service settings
|
||||
* ================
|
||||
*/
|
||||
/* pg_ctl_options */
|
||||
{
|
||||
"pg_ctl_options",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_ctl_options },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_ctl_options) },
|
||||
{}
|
||||
},
|
||||
/* service_start_command */
|
||||
{
|
||||
"service_start_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.service_start_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.service_start_command) },
|
||||
{}
|
||||
},
|
||||
/* service_stop_command */
|
||||
{
|
||||
"service_stop_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.service_stop_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.service_stop_command) },
|
||||
{}
|
||||
},
|
||||
/* service_restart_command */
|
||||
{
|
||||
"service_restart_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.service_restart_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.service_restart_command) },
|
||||
{}
|
||||
},
|
||||
/* service_reload_command */
|
||||
{
|
||||
"service_reload_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.service_reload_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.service_reload_command) },
|
||||
{}
|
||||
},
|
||||
/* service_promote_command */
|
||||
{
|
||||
"service_promote_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.service_promote_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.service_promote_command) },
|
||||
{}
|
||||
},
|
||||
|
||||
/* ========================
|
||||
* repmgrd service settings
|
||||
* ========================
|
||||
*/
|
||||
/* repmgrd_service_start_command */
|
||||
{
|
||||
"repmgrd_service_start_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.repmgrd_service_start_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.repmgrd_service_start_command) },
|
||||
{}
|
||||
},
|
||||
/* repmgrd_service_stop_command */
|
||||
{
|
||||
"repmgrd_service_stop_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.repmgrd_service_stop_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.repmgrd_service_stop_command) },
|
||||
{}
|
||||
},
|
||||
/* ===========================
|
||||
* event notification settings
|
||||
* ===========================
|
||||
*/
|
||||
/* event_notification_command */
|
||||
{
|
||||
"event_notification_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.event_notification_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.event_notification_command) },
|
||||
{}
|
||||
},
|
||||
{
|
||||
"event_notifications",
|
||||
CONFIG_EVENT_NOTIFICATION_LIST,
|
||||
{ .notificationlistptr = &config_file_options.event_notifications },
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* ===============
|
||||
* barman settings
|
||||
* ===============
|
||||
*/
|
||||
/* barman_host */
|
||||
{
|
||||
"barman_host",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.barman_host },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.barman_host) },
|
||||
{}
|
||||
},
|
||||
/* barman_server */
|
||||
{
|
||||
"barman_server",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.barman_server },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.barman_server) },
|
||||
{}
|
||||
},
|
||||
/* barman_config */
|
||||
{
|
||||
"barman_config",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.barman_config },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.barman_config) },
|
||||
{}
|
||||
},
|
||||
/* ==================
|
||||
* rsync/ssh settings
|
||||
* ==================
|
||||
*/
|
||||
/* rsync_options */
|
||||
{
|
||||
"rsync_options",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.rsync_options },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.rsync_options) },
|
||||
{}
|
||||
},
|
||||
/* ssh_options */
|
||||
{
|
||||
"ssh_options",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.ssh_options },
|
||||
{ .strdefault = DEFAULT_SSH_OPTIONS },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.ssh_options) },
|
||||
{}
|
||||
},
|
||||
/* ==================================
|
||||
* undocumented experimental settings
|
||||
* ==================================
|
||||
*/
|
||||
/* reconnect_loop_sync */
|
||||
{
|
||||
"reconnect_loop_sync",
|
||||
CONFIG_BOOL,
|
||||
{ .boolptr = &config_file_options.reconnect_loop_sync },
|
||||
{ .booldefault = false },
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* ==========================
|
||||
* undocumented test settings
|
||||
* ==========================
|
||||
*/
|
||||
/* promote_delay */
|
||||
{
|
||||
"promote_delay",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.promote_delay },
|
||||
{ .intdefault = 0 },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* failover_delay */
|
||||
{
|
||||
"failover_delay",
|
||||
CONFIG_INT,
|
||||
{ .intptr = &config_file_options.failover_delay },
|
||||
{ .intdefault = 0 },
|
||||
{ .intminval = 1 },
|
||||
{},
|
||||
{}
|
||||
},
|
||||
{
|
||||
"connection_check_query",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.connection_check_query },
|
||||
{ .strdefault = "SELECT 1" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.connection_check_query) },
|
||||
{}
|
||||
},
|
||||
/* End-of-list marker */
|
||||
{
|
||||
NULL, CONFIG_INT, {}, {}, {}, {}, {}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
%{
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "configfile.h"
|
||||
@@ -38,7 +40,13 @@ static sigjmp_buf *CONF_flex_fatal_jmp;
|
||||
static char *CONF_scanstr(const char *s);
|
||||
static int CONF_flex_fatal(const char *msg);
|
||||
|
||||
static bool ProcessConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, t_configuration_options *options, ItemList *error_list, ItemList *warning_list);
|
||||
static bool ProcessConfigFile(const char *base_dir, const char *config_file, const char *calling_file, bool strict, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
|
||||
|
||||
static bool ProcessConfigFp(FILE *fp, const char *config_file, const char *calling_file, int depth, const char *base_dir, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
|
||||
|
||||
static bool ProcessConfigDirectory(const char *base_dir, const char *includedir, const char *calling_file, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
|
||||
|
||||
static char *AbsoluteConfigLocation(const char *base_dir, const char *location, const char *calling_file);
|
||||
|
||||
%}
|
||||
|
||||
@@ -90,20 +98,91 @@ STRING \'([^'\\\n]|\\.|\'\')*\'
|
||||
|
||||
%%
|
||||
|
||||
extern bool
|
||||
ProcessRepmgrConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list)
|
||||
{
|
||||
return ProcessConfigFile(fp, config_file, NULL, options, error_list, warning_list);
|
||||
}
|
||||
|
||||
extern bool
|
||||
ProcessPostgresConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
|
||||
ProcessRepmgrConfigFile(const char *config_file, const char *base_dir, ItemList *error_list, ItemList *warning_list)
|
||||
{
|
||||
return ProcessConfigFile(fp, config_file, contents, NULL, error_list, warning_list);
|
||||
return ProcessConfigFile(base_dir, config_file, NULL, true, 0, NULL, error_list, warning_list);
|
||||
}
|
||||
|
||||
|
||||
extern bool
|
||||
ProcessPostgresConfigFile(const char *config_file, const char *base_dir, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
|
||||
{
|
||||
return ProcessConfigFile(base_dir, config_file, NULL, true, 0, contents, error_list, warning_list);
|
||||
}
|
||||
|
||||
static bool
|
||||
ProcessConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, t_configuration_options *options, ItemList *error_list, ItemList *warning_list)
|
||||
ProcessConfigFile(const char *base_dir, const char *config_file, const char *calling_file, bool strict, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
|
||||
{
|
||||
char *abs_path;
|
||||
bool success = true;
|
||||
FILE *fp;
|
||||
|
||||
/*
|
||||
* Reject file name that is all-blank (including empty), as that leads to
|
||||
* confusion --- we'd try to read the containing directory as a file.
|
||||
*/
|
||||
if (strspn(config_file, " \t\r\n") == strlen(config_file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reject too-deep include nesting depth. This is just a safety check to
|
||||
* avoid dumping core due to stack overflow if an include file loops back
|
||||
* to itself. The maximum nesting depth is pretty arbitrary.
|
||||
*/
|
||||
if (depth > 10)
|
||||
{
|
||||
item_list_append_format(error_list,
|
||||
_("could not open configuration file \"%s\": maximum nesting depth exceeded"),
|
||||
config_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
abs_path = AbsoluteConfigLocation(base_dir, config_file, calling_file);
|
||||
|
||||
/* Reject direct recursion */
|
||||
if (calling_file && strcmp(abs_path, calling_file) == 0)
|
||||
{
|
||||
item_list_append_format(error_list,
|
||||
_("configuration file recursion in \"%s\""),
|
||||
calling_file);
|
||||
pfree(abs_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
fp = fopen(abs_path, "r");
|
||||
if (!fp)
|
||||
{
|
||||
if (strict == false)
|
||||
{
|
||||
item_list_append_format(error_list,
|
||||
"skipping configuration file \"%s\"",
|
||||
abs_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
item_list_append_format(error_list,
|
||||
"could not open configuration file \"%s\": %s",
|
||||
abs_path,
|
||||
strerror(errno));
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = ProcessConfigFp(fp, abs_path, calling_file, depth + 1, base_dir, contents, error_list, warning_list);
|
||||
}
|
||||
|
||||
free(abs_path);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool
|
||||
ProcessConfigFp(FILE *fp, const char *config_file, const char *calling_file, int depth, const char *base_dir, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
|
||||
{
|
||||
volatile bool OK = true;
|
||||
volatile YY_BUFFER_STATE lex_buffer = NULL;
|
||||
@@ -179,22 +258,62 @@ ProcessConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, t_c
|
||||
ConfigFileLineno++;
|
||||
}
|
||||
|
||||
/* OK, process the option name and value */
|
||||
if (contents != NULL)
|
||||
/* Handle include files */
|
||||
if (base_dir != NULL && strcasecmp(opt_name, "include_dir") == 0)
|
||||
{
|
||||
key_value_list_replace_or_set(contents,
|
||||
opt_name,
|
||||
opt_value);
|
||||
/*
|
||||
* An include_dir directive isn't a variable and should be
|
||||
* processed immediately.
|
||||
*/
|
||||
if (!ProcessConfigDirectory(base_dir, opt_value, config_file,
|
||||
depth + 1, contents,
|
||||
error_list, warning_list))
|
||||
OK = false;
|
||||
yy_switch_to_buffer(lex_buffer);
|
||||
pfree(opt_name);
|
||||
pfree(opt_value);
|
||||
}
|
||||
else if (base_dir != NULL && strcasecmp(opt_name, "include_if_exists") == 0)
|
||||
{
|
||||
if (!ProcessConfigFile(base_dir, opt_value, config_file,
|
||||
false, depth + 1, contents,
|
||||
error_list, warning_list))
|
||||
OK = false;
|
||||
|
||||
yy_switch_to_buffer(lex_buffer);
|
||||
pfree(opt_name);
|
||||
pfree(opt_value);
|
||||
}
|
||||
else if (base_dir != NULL && strcasecmp(opt_name, "include") == 0)
|
||||
{
|
||||
if (!ProcessConfigFile(base_dir, opt_value, config_file,
|
||||
true, depth + 1, contents,
|
||||
error_list, warning_list))
|
||||
OK = false;
|
||||
|
||||
yy_switch_to_buffer(lex_buffer);
|
||||
pfree(opt_name);
|
||||
pfree(opt_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* OK, process the option name and value */
|
||||
if (contents != NULL)
|
||||
{
|
||||
key_value_list_replace_or_set(contents,
|
||||
opt_name,
|
||||
opt_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_configuration_item(error_list,
|
||||
warning_list,
|
||||
opt_name,
|
||||
opt_value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (options != NULL)
|
||||
{
|
||||
parse_configuration_item(options,
|
||||
error_list,
|
||||
warning_list,
|
||||
opt_name,
|
||||
opt_value);
|
||||
}
|
||||
|
||||
/* break out of loop if read EOF, else loop for next line */
|
||||
if (token == 0)
|
||||
@@ -253,6 +372,132 @@ cleanup:
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and parse all config files in a subdirectory in alphabetical order
|
||||
*
|
||||
* includedir is the absolute or relative path to the subdirectory to scan.
|
||||
*
|
||||
* See ProcessConfigFp for further details.
|
||||
*/
|
||||
static bool
|
||||
ProcessConfigDirectory(const char *base_dir, const char *includedir, const char *calling_file, int depth, KeyValueList *contents, ItemList *error_list, ItemList *warning_list)
|
||||
{
|
||||
char *directory;
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
char **filenames;
|
||||
int num_filenames;
|
||||
int size_filenames;
|
||||
bool status;
|
||||
|
||||
/*
|
||||
* Reject directory name that is all-blank (including empty), as that
|
||||
* leads to confusion --- we'd read the containing directory, typically
|
||||
* resulting in recursive inclusion of the same file(s).
|
||||
*/
|
||||
if (strspn(includedir, " \t\r\n") == strlen(includedir))
|
||||
{
|
||||
item_list_append_format(error_list,
|
||||
_("empty configuration directory name: \"%s\""),
|
||||
includedir);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
directory = AbsoluteConfigLocation(base_dir, includedir, calling_file);
|
||||
d = opendir(directory);
|
||||
if (d == NULL)
|
||||
{
|
||||
item_list_append_format(error_list,
|
||||
_("could not open configuration directory \"%s\": %s"),
|
||||
directory,
|
||||
strerror(errno));
|
||||
status = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the directory and put the filenames in an array, so we can sort
|
||||
* them prior to processing the contents.
|
||||
*/
|
||||
size_filenames = 32;
|
||||
filenames = (char **) palloc(size_filenames * sizeof(char *));
|
||||
num_filenames = 0;
|
||||
|
||||
while ((de = readdir(d)) != NULL)
|
||||
{
|
||||
struct stat st;
|
||||
char filename[MAXPGPATH];
|
||||
|
||||
/*
|
||||
* Only parse files with names ending in ".conf". Explicitly reject
|
||||
* files starting with ".". This excludes things like "." and "..",
|
||||
* as well as typical hidden files, backup files, and editor debris.
|
||||
*/
|
||||
if (strlen(de->d_name) < 6)
|
||||
continue;
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
|
||||
continue;
|
||||
|
||||
join_path_components(filename, directory, de->d_name);
|
||||
canonicalize_path(filename);
|
||||
if (stat(filename, &st) == 0)
|
||||
{
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
{
|
||||
/* Add file to array, increasing its size in blocks of 32 */
|
||||
if (num_filenames >= size_filenames)
|
||||
{
|
||||
size_filenames += 32;
|
||||
filenames = (char **) repalloc(filenames,
|
||||
size_filenames * sizeof(char *));
|
||||
}
|
||||
filenames[num_filenames] = pstrdup(filename);
|
||||
num_filenames++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* stat does not care about permissions, so the most likely reason
|
||||
* a file can't be accessed now is if it was removed between the
|
||||
* directory listing and now.
|
||||
*/
|
||||
item_list_append_format(error_list,
|
||||
_("could not stat file \"%s\": %s"),
|
||||
filename, strerror(errno));
|
||||
status = false;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_filenames > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
|
||||
for (i = 0; i < num_filenames; i++)
|
||||
{
|
||||
if (!ProcessConfigFile(base_dir, filenames[i], calling_file,
|
||||
true, depth, contents,
|
||||
error_list, warning_list))
|
||||
{
|
||||
status = false;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
status = true;
|
||||
|
||||
|
||||
cleanup:
|
||||
if (d)
|
||||
closedir(d);
|
||||
pfree(directory);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* scanstr
|
||||
@@ -348,6 +593,39 @@ CONF_scanstr(const char *s)
|
||||
return newStr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a configuration file or directory location that may be a relative
|
||||
* path, return an absolute one. We consider the location to be relative to
|
||||
* the directory holding the calling file, or to DataDir if no calling file.
|
||||
*/
|
||||
static char *
|
||||
AbsoluteConfigLocation(const char *base_dir, const char *location, const char *calling_file)
|
||||
{
|
||||
char abs_path[MAXPGPATH];
|
||||
|
||||
if (is_absolute_path(location))
|
||||
return strdup(location);
|
||||
|
||||
if (calling_file != NULL)
|
||||
{
|
||||
strlcpy(abs_path, calling_file, sizeof(abs_path));
|
||||
get_parent_directory(abs_path);
|
||||
join_path_components(abs_path, abs_path, location);
|
||||
canonicalize_path(abs_path);
|
||||
}
|
||||
else if (base_dir != NULL)
|
||||
{
|
||||
join_path_components(abs_path, base_dir, location);
|
||||
canonicalize_path(abs_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
strlcpy(abs_path, location, sizeof(abs_path));
|
||||
}
|
||||
|
||||
return strdup(abs_path);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flex fatal errors bring us here. Stash the error message and jump back to
|
||||
|
||||
1688
configfile.c
1688
configfile.c
File diff suppressed because it is too large
Load Diff
156
configfile.h
156
configfile.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* configfile.h
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -29,7 +29,7 @@
|
||||
#define TARGET_TIMELINE_LATEST 0
|
||||
|
||||
/*
|
||||
* This is defined src/include/utils.h, however it's not practical
|
||||
* This is defined in src/include/utils.h, however it's not practical
|
||||
* to include that from a frontend application.
|
||||
*/
|
||||
#define PG_AUTOCONF_FILENAME "postgresql.auto.conf"
|
||||
@@ -78,6 +78,55 @@ typedef struct TablespaceList
|
||||
} TablespaceList;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONFIG_BOOL,
|
||||
CONFIG_INT,
|
||||
CONFIG_STRING,
|
||||
CONFIG_FAILOVER_MODE,
|
||||
CONFIG_CONNECTION_CHECK_TYPE,
|
||||
CONFIG_EVENT_NOTIFICATION_LIST,
|
||||
CONFIG_TABLESPACE_MAPPING
|
||||
} ConfigItemType;
|
||||
|
||||
|
||||
typedef struct ConfigFileSetting
|
||||
{
|
||||
const char *name;
|
||||
ConfigItemType type;
|
||||
union
|
||||
{
|
||||
int *intptr;
|
||||
char *strptr;
|
||||
bool *boolptr;
|
||||
failover_mode_opt *failovermodeptr;
|
||||
ConnectionCheckType *checktypeptr;
|
||||
EventNotificationList *notificationlistptr;
|
||||
TablespaceList *tablespacemappingptr;
|
||||
} val;
|
||||
union {
|
||||
int intdefault;
|
||||
const char *strdefault;
|
||||
bool booldefault;
|
||||
failover_mode_opt failovermodedefault;
|
||||
ConnectionCheckType checktypedefault;
|
||||
} defval;
|
||||
union {
|
||||
int intminval;
|
||||
} minval;
|
||||
union {
|
||||
int strmaxlen;
|
||||
} maxval;
|
||||
struct {
|
||||
void (*process_func)(const char *, const char *, char *, ItemList *errors);
|
||||
void (*postprocess_func)(const char *, const char *, char *, ItemList *errors);
|
||||
bool *providedptr;
|
||||
} process;
|
||||
} ConfigFileSetting;
|
||||
|
||||
/* Declare the main configfile structure for client applications */
|
||||
extern ConfigFileSetting config_file_settings[];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* node information */
|
||||
@@ -115,6 +164,7 @@ typedef struct
|
||||
/* standby follow settings */
|
||||
int primary_follow_timeout;
|
||||
int standby_follow_timeout;
|
||||
bool standby_follow_restart;
|
||||
|
||||
/* standby switchover settings */
|
||||
int shutdown_check_timeout;
|
||||
@@ -152,6 +202,7 @@ typedef struct
|
||||
int sibling_nodes_disconnect_timeout;
|
||||
ConnectionCheckType connection_check_type;
|
||||
bool primary_visibility_consensus;
|
||||
bool always_promote;
|
||||
char failover_validation_command[MAXPGPATH];
|
||||
int election_rerun_interval;
|
||||
int child_nodes_check_interval;
|
||||
@@ -161,10 +212,6 @@ typedef struct
|
||||
int child_nodes_disconnect_timeout;
|
||||
char child_nodes_disconnect_command[MAXPGPATH];
|
||||
|
||||
/* BDR settings */
|
||||
bool bdr_local_monitoring_only;
|
||||
bool bdr_recovery_timeout;
|
||||
|
||||
/* service settings */
|
||||
char pg_ctl_options[MAXLEN];
|
||||
char service_start_command[MAXPGPATH];
|
||||
@@ -191,79 +238,35 @@ typedef struct
|
||||
char rsync_options[MAXLEN];
|
||||
char ssh_options[MAXLEN];
|
||||
|
||||
/* undocumented test settings */
|
||||
/*
|
||||
* undocumented settings
|
||||
*
|
||||
* These settings are for testing or experimential features
|
||||
* and may be changed without notice.
|
||||
*/
|
||||
|
||||
/* experimental settings */
|
||||
bool reconnect_loop_sync;
|
||||
|
||||
/* test settings */
|
||||
int promote_delay;
|
||||
int failover_delay;
|
||||
char connection_check_query[MAXLEN];
|
||||
} t_configuration_options;
|
||||
|
||||
/*
|
||||
* The following will initialize the structure with a minimal set of options;
|
||||
* actual defaults are set in parse_config() before parsing the configuration file
|
||||
*/
|
||||
|
||||
#define T_CONFIGURATION_OPTIONS_INITIALIZER { \
|
||||
/* node information */ \
|
||||
UNKNOWN_NODE_ID, "", "", "", "", "", "", "", REPLICATION_TYPE_PHYSICAL, \
|
||||
/* log settings */ \
|
||||
"", "", "", DEFAULT_LOG_STATUS_INTERVAL, \
|
||||
/* standby clone settings */ \
|
||||
false, "", "", { NULL, NULL }, "", false, "", false, "", \
|
||||
/* standby promote settings */ \
|
||||
DEFAULT_PROMOTE_CHECK_TIMEOUT, DEFAULT_PROMOTE_CHECK_INTERVAL, \
|
||||
/* standby follow settings */ \
|
||||
DEFAULT_PRIMARY_FOLLOW_TIMEOUT, \
|
||||
DEFAULT_STANDBY_FOLLOW_TIMEOUT, \
|
||||
/* standby switchover settings */ \
|
||||
DEFAULT_SHUTDOWN_CHECK_TIMEOUT, \
|
||||
DEFAULT_STANDBY_RECONNECT_TIMEOUT, \
|
||||
DEFAULT_WAL_RECEIVE_CHECK_TIMEOUT, \
|
||||
/* node rejoin settings */ \
|
||||
DEFAULT_NODE_REJOIN_TIMEOUT, \
|
||||
/* node check settings */ \
|
||||
DEFAULT_ARCHIVE_READY_WARNING, DEFAULT_ARCHIVE_READY_CRITICAL, \
|
||||
DEFAULT_REPLICATION_LAG_WARNING, DEFAULT_REPLICATION_LAG_CRITICAL, \
|
||||
/* witness settings */ \
|
||||
DEFAULT_WITNESS_SYNC_INTERVAL, \
|
||||
/* repmgrd settings */ \
|
||||
FAILOVER_MANUAL, DEFAULT_LOCATION, DEFAULT_PRIORITY, "", "", \
|
||||
DEFAULT_MONITORING_INTERVAL, \
|
||||
DEFAULT_RECONNECTION_ATTEMPTS, \
|
||||
DEFAULT_RECONNECTION_INTERVAL, \
|
||||
false, -1, \
|
||||
DEFAULT_ASYNC_QUERY_TIMEOUT, \
|
||||
DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT, \
|
||||
-1, "", false, DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT, \
|
||||
CHECK_PING, true, "", DEFAULT_ELECTION_RERUN_INTERVAL, \
|
||||
DEFAULT_CHILD_NODES_CHECK_INTERVAL, \
|
||||
DEFAULT_CHILD_NODES_DISCONNECT_MIN_COUNT, \
|
||||
DEFAULT_CHILD_NODES_CONNECTED_MIN_COUNT, \
|
||||
DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS, \
|
||||
DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT, "", \
|
||||
/* BDR settings */ \
|
||||
false, DEFAULT_BDR_RECOVERY_TIMEOUT, \
|
||||
/* service settings */ \
|
||||
"", "", "", "", "", "", \
|
||||
/* repmgrd service settings */ \
|
||||
"", "", \
|
||||
/* event notification settings */ \
|
||||
"", "", { NULL, NULL }, \
|
||||
/* barman settings */ \
|
||||
"", "", "", \
|
||||
/* rsync/ssh settings */ \
|
||||
"", "", \
|
||||
/* undocumented test settings */ \
|
||||
0 \
|
||||
}
|
||||
|
||||
|
||||
/* Declare the main configfile structure for client applications */
|
||||
extern t_configuration_options config_file_options;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char slot[MAXLEN];
|
||||
char wal_method[MAXLEN];
|
||||
char waldir[MAXPGPATH];
|
||||
bool no_slot; /* from PostgreSQL 10 */
|
||||
} t_basebackup_options;
|
||||
|
||||
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", false }
|
||||
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", "", false }
|
||||
|
||||
|
||||
typedef enum
|
||||
@@ -320,10 +323,11 @@ typedef struct
|
||||
void set_progname(const char *argv0);
|
||||
const char *progname(void);
|
||||
|
||||
void load_config(const char *config_file, bool verbose, bool terse, t_configuration_options *options, char *argv0);
|
||||
bool reload_config(t_configuration_options *orig_options, t_server_type server_type);
|
||||
void load_config(const char *config_file, bool verbose, bool terse, char *argv0);
|
||||
bool reload_config(t_server_type server_type);
|
||||
void dump_config(void);
|
||||
|
||||
void parse_configuration_item(t_configuration_options *options, ItemList *error_list, ItemList *warning_list, const char *name, const char *value);
|
||||
void parse_configuration_item(ItemList *error_list, ItemList *warning_list, const char *name, const char *value);
|
||||
|
||||
bool parse_recovery_conf(const char *data_dir, t_recovery_conf *conf);
|
||||
|
||||
@@ -336,6 +340,9 @@ int repmgr_atoi(const char *s,
|
||||
ItemList *error_list,
|
||||
int minval);
|
||||
|
||||
void parse_time_unit_parameter(const char *name, const char *value, char *dest, ItemList *errors);
|
||||
void repmgr_canonicalize_path(const char *name, const char *value, char *config_item, ItemList *errors);
|
||||
|
||||
bool parse_pg_basebackup_options(const char *pg_basebackup_options,
|
||||
t_basebackup_options *backup_options,
|
||||
int server_version_num,
|
||||
@@ -343,17 +350,20 @@ bool parse_pg_basebackup_options(const char *pg_basebackup_options,
|
||||
|
||||
int parse_output_to_argv(const char *string, char ***argv_array);
|
||||
void free_parsed_argv(char ***argv_array);
|
||||
|
||||
const char *format_failover_mode(failover_mode_opt failover);
|
||||
|
||||
/* called by repmgr-client and repmgrd */
|
||||
void exit_with_cli_errors(ItemList *error_list, const char *repmgr_command);
|
||||
|
||||
void print_item_list(ItemList *item_list);
|
||||
const char *print_connection_check_type(ConnectionCheckType type);
|
||||
char *print_event_notification_list(EventNotificationList *list);
|
||||
char *print_tablespace_mapping(TablespaceList *tablespacemappingptr);
|
||||
|
||||
extern bool modify_auto_conf(const char *data_dir, KeyValueList *items);
|
||||
|
||||
extern bool ProcessRepmgrConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list);
|
||||
extern bool ProcessRepmgrConfigFile(const char *config_file, const char *base_dir, ItemList *error_list, ItemList *warning_list);
|
||||
|
||||
extern bool ProcessPostgresConfigFile(FILE *fp, const char *config_file, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
|
||||
extern bool ProcessPostgresConfigFile(const char *config_file, const char *base_dir, KeyValueList *contents, ItemList *error_list, ItemList *warning_list);
|
||||
|
||||
#endif /* _REPMGR_CONFIGFILE_H_ */
|
||||
|
||||
24
configure
vendored
24
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for repmgr 5.0.0.
|
||||
# Generated by GNU Autoconf 2.69 for repmgr 5.2.0.
|
||||
#
|
||||
# Report bugs to <repmgr@googlegroups.com>.
|
||||
#
|
||||
@@ -11,7 +11,7 @@
|
||||
# This configure script is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy, distribute and modify it.
|
||||
#
|
||||
# Copyright (c) 2010-2019, 2ndQuadrant Ltd.
|
||||
# Copyright (c) 2010-2020, 2ndQuadrant Ltd.
|
||||
## -------------------- ##
|
||||
## M4sh Initialization. ##
|
||||
## -------------------- ##
|
||||
@@ -582,8 +582,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='repmgr'
|
||||
PACKAGE_TARNAME='repmgr'
|
||||
PACKAGE_VERSION='5.0.0'
|
||||
PACKAGE_STRING='repmgr 5.0.0'
|
||||
PACKAGE_VERSION='5.2.0'
|
||||
PACKAGE_STRING='repmgr 5.2.0'
|
||||
PACKAGE_BUGREPORT='repmgr@googlegroups.com'
|
||||
PACKAGE_URL='https://repmgr.org/'
|
||||
|
||||
@@ -1181,7 +1181,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures repmgr 5.0.0 to adapt to many kinds of systems.
|
||||
\`configure' configures repmgr 5.2.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1242,7 +1242,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of repmgr 5.0.0:";;
|
||||
short | recursive ) echo "Configuration of repmgr 5.2.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1316,14 +1316,14 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
repmgr configure 5.0.0
|
||||
repmgr configure 5.2.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
This configure script is free software; the Free Software Foundation
|
||||
gives unlimited permission to copy, distribute and modify it.
|
||||
|
||||
Copyright (c) 2010-2019, 2ndQuadrant Ltd.
|
||||
Copyright (c) 2010-2020, 2ndQuadrant Ltd.
|
||||
_ACEOF
|
||||
exit
|
||||
fi
|
||||
@@ -1335,7 +1335,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by repmgr $as_me 5.0.0, which was
|
||||
It was created by repmgr $as_me 5.2.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -1824,7 +1824,7 @@ if test "$major_version_num" -lt '10'; then
|
||||
version_num_int=$(echo "$version_num"|
|
||||
$SED -e 's/^\([0-9]*\)\.\([0-9]*\)$/\1\2/')
|
||||
|
||||
if test "$version_num_int" -lt '93'; then
|
||||
if test "$version_num_int" -lt '94'; then
|
||||
as_fn_error $? "repmgr is not compatible with detected PostgreSQL version: $version_num" "$LINENO" 5
|
||||
fi
|
||||
else
|
||||
@@ -2487,7 +2487,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by repmgr $as_me 5.0.0, which was
|
||||
This file was extended by repmgr $as_me 5.2.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -2550,7 +2550,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
repmgr config.status 5.0.0
|
||||
repmgr config.status 5.2.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
AC_INIT([repmgr], [5.0.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
|
||||
AC_INIT([repmgr], [5.2.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
|
||||
|
||||
AC_COPYRIGHT([Copyright (c) 2010-2019, 2ndQuadrant Ltd.])
|
||||
AC_COPYRIGHT([Copyright (c) 2010-2020, 2ndQuadrant Ltd.])
|
||||
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
||||
@@ -32,7 +32,7 @@ if test "$major_version_num" -lt '10'; then
|
||||
version_num_int=$(echo "$version_num"|
|
||||
$SED -e 's/^\([[0-9]]*\)\.\([[0-9]]*\)$/\1\2/')
|
||||
|
||||
if test "$version_num_int" -lt '93'; then
|
||||
if test "$version_num_int" -lt '94'; then
|
||||
AC_MSG_ERROR([repmgr is not compatible with detected PostgreSQL version: $version_num])
|
||||
fi
|
||||
else
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* running. For that reason we can't use on the pg_control_*() functions
|
||||
* provided in PostgreSQL 9.6 and later.
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
@@ -90,7 +90,9 @@ get_system_identifier(const char *data_directory)
|
||||
uint64 system_identifier = UNKNOWN_SYSTEM_IDENTIFIER;
|
||||
|
||||
control_file_info = get_controlfile(data_directory);
|
||||
system_identifier = control_file_info->system_identifier;
|
||||
|
||||
if (control_file_info->control_file_processed == true)
|
||||
system_identifier = control_file_info->system_identifier;
|
||||
|
||||
pfree(control_file_info);
|
||||
|
||||
@@ -98,19 +100,21 @@ get_system_identifier(const char *data_directory)
|
||||
}
|
||||
|
||||
|
||||
DBState
|
||||
get_db_state(const char *data_directory)
|
||||
bool
|
||||
get_db_state(const char *data_directory, DBState *state)
|
||||
{
|
||||
ControlFileInfo *control_file_info = NULL;
|
||||
DBState state;
|
||||
bool control_file_processed;
|
||||
|
||||
control_file_info = get_controlfile(data_directory);
|
||||
control_file_processed = control_file_info->control_file_processed;
|
||||
|
||||
state = control_file_info->state;
|
||||
if (control_file_processed == true)
|
||||
*state = control_file_info->state;
|
||||
|
||||
pfree(control_file_info);
|
||||
|
||||
return state;
|
||||
return control_file_processed;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +126,8 @@ get_latest_checkpoint_location(const char *data_directory)
|
||||
|
||||
control_file_info = get_controlfile(data_directory);
|
||||
|
||||
checkPoint = control_file_info->checkPoint;
|
||||
if (control_file_info->control_file_processed == true)
|
||||
checkPoint = control_file_info->checkPoint;
|
||||
|
||||
pfree(control_file_info);
|
||||
|
||||
@@ -134,11 +139,12 @@ int
|
||||
get_data_checksum_version(const char *data_directory)
|
||||
{
|
||||
ControlFileInfo *control_file_info = NULL;
|
||||
int data_checksum_version = -1;
|
||||
int data_checksum_version = UNKNOWN_DATA_CHECKSUM_VERSION;
|
||||
|
||||
control_file_info = get_controlfile(data_directory);
|
||||
|
||||
data_checksum_version = (int) control_file_info->data_checksum_version;
|
||||
if (control_file_info->control_file_processed == true)
|
||||
data_checksum_version = (int) control_file_info->data_checksum_version;
|
||||
|
||||
pfree(control_file_info);
|
||||
|
||||
@@ -277,8 +283,19 @@ get_controlfile(const char *DataDir)
|
||||
return control_file_info;
|
||||
}
|
||||
|
||||
|
||||
if (version_num >= 90500)
|
||||
if (version_num >= 120000)
|
||||
{
|
||||
#if PG_ACTUAL_VERSION_NUM >= 120000
|
||||
expected_size = sizeof(ControlFileData12);
|
||||
ControlFileDataPtr = palloc0(expected_size);
|
||||
#endif
|
||||
}
|
||||
else if (version_num >= 110000)
|
||||
{
|
||||
expected_size = sizeof(ControlFileData11);
|
||||
ControlFileDataPtr = palloc0(expected_size);
|
||||
}
|
||||
else if (version_num >= 90500)
|
||||
{
|
||||
expected_size = sizeof(ControlFileData95);
|
||||
ControlFileDataPtr = palloc0(expected_size);
|
||||
@@ -288,12 +305,6 @@ get_controlfile(const char *DataDir)
|
||||
expected_size = sizeof(ControlFileData94);
|
||||
ControlFileDataPtr = palloc0(expected_size);
|
||||
}
|
||||
else if (version_num >= 90300)
|
||||
{
|
||||
expected_size = sizeof(ControlFileData93);
|
||||
ControlFileDataPtr = palloc0(expected_size);
|
||||
}
|
||||
|
||||
|
||||
if (read(fd, ControlFileDataPtr, expected_size) != expected_size)
|
||||
{
|
||||
@@ -359,17 +370,6 @@ get_controlfile(const char *DataDir)
|
||||
control_file_info->minRecoveryPointTLI = ptr->minRecoveryPointTLI;
|
||||
control_file_info->minRecoveryPoint = ptr->minRecoveryPoint;
|
||||
}
|
||||
else if (version_num >= 90300)
|
||||
{
|
||||
ControlFileData93 *ptr = (struct ControlFileData93 *)ControlFileDataPtr;
|
||||
control_file_info->system_identifier = ptr->system_identifier;
|
||||
control_file_info->state = ptr->state;
|
||||
control_file_info->checkPoint = ptr->checkPoint;
|
||||
control_file_info->data_checksum_version = ptr->data_checksum_version;
|
||||
control_file_info->timeline = ptr->checkPointCopy.ThisTimeLineID;
|
||||
control_file_info->minRecoveryPointTLI = ptr->minRecoveryPointTLI;
|
||||
control_file_info->minRecoveryPoint = ptr->minRecoveryPoint;
|
||||
}
|
||||
|
||||
pfree(ControlFileDataPtr);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* controldata.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
@@ -31,9 +31,7 @@ typedef struct
|
||||
} ControlFileInfo;
|
||||
|
||||
|
||||
|
||||
/* Same for 9.3, 9.4 */
|
||||
typedef struct CheckPoint93
|
||||
typedef struct CheckPoint94
|
||||
{
|
||||
XLogRecPtr redo; /* next RecPtr available when we began to
|
||||
* create CheckPoint (i.e. REDO start point) */
|
||||
@@ -53,7 +51,7 @@ typedef struct CheckPoint93
|
||||
pg_time_t time; /* time stamp of checkpoint */
|
||||
|
||||
TransactionId oldestActiveXid;
|
||||
} CheckPoint93;
|
||||
} CheckPoint94;
|
||||
|
||||
|
||||
/* Same for 9.5, 9.6, 10, 11 */
|
||||
@@ -128,65 +126,6 @@ typedef struct CheckPoint12
|
||||
} CheckPoint12;
|
||||
#endif
|
||||
|
||||
typedef struct ControlFileData93
|
||||
{
|
||||
uint64 system_identifier;
|
||||
|
||||
uint32 pg_control_version; /* PG_CONTROL_VERSION */
|
||||
uint32 catalog_version_no; /* see catversion.h */
|
||||
|
||||
DBState state; /* see enum above */
|
||||
pg_time_t time; /* time stamp of last pg_control update */
|
||||
XLogRecPtr checkPoint; /* last check point record ptr */
|
||||
XLogRecPtr prevCheckPoint; /* previous check point record ptr */
|
||||
|
||||
CheckPoint93 checkPointCopy; /* copy of last check point record */
|
||||
|
||||
XLogRecPtr unloggedLSN; /* current fake LSN value, for unlogged rels */
|
||||
|
||||
XLogRecPtr minRecoveryPoint;
|
||||
TimeLineID minRecoveryPointTLI;
|
||||
XLogRecPtr backupStartPoint;
|
||||
XLogRecPtr backupEndPoint;
|
||||
bool backupEndRequired;
|
||||
|
||||
int wal_level;
|
||||
int MaxConnections;
|
||||
int max_prepared_xacts;
|
||||
int max_locks_per_xact;
|
||||
|
||||
uint32 maxAlign; /* alignment requirement for tuples */
|
||||
double floatFormat; /* constant 1234567.0 */
|
||||
|
||||
uint32 blcksz; /* data block size for this DB */
|
||||
uint32 relseg_size; /* blocks per segment of large relation */
|
||||
|
||||
uint32 xlog_blcksz; /* block size within WAL files */
|
||||
uint32 xlog_seg_size; /* size of each WAL segment */
|
||||
|
||||
uint32 nameDataLen; /* catalog name field width */
|
||||
uint32 indexMaxKeys; /* max number of columns in an index */
|
||||
|
||||
uint32 toast_max_chunk_size; /* chunk size in TOAST tables */
|
||||
|
||||
/* flag indicating internal format of timestamp, interval, time */
|
||||
bool enableIntTimes; /* int64 storage enabled? */
|
||||
|
||||
/* flags indicating pass-by-value status of various types */
|
||||
bool float4ByVal; /* float4 pass-by-value? */
|
||||
bool float8ByVal; /* float8, int8, etc pass-by-value? */
|
||||
|
||||
/* Are data pages protected by checksums? Zero if no checksum version */
|
||||
uint32 data_checksum_version;
|
||||
|
||||
} ControlFileData93;
|
||||
|
||||
|
||||
/*
|
||||
* Following field added since 9.3:
|
||||
*
|
||||
* int max_worker_processes;
|
||||
*/
|
||||
|
||||
typedef struct ControlFileData94
|
||||
{
|
||||
@@ -200,7 +139,7 @@ typedef struct ControlFileData94
|
||||
XLogRecPtr checkPoint; /* last check point record ptr */
|
||||
XLogRecPtr prevCheckPoint; /* previous check point record ptr */
|
||||
|
||||
CheckPoint93 checkPointCopy; /* copy of last check point record */
|
||||
CheckPoint94 checkPointCopy; /* copy of last check point record */
|
||||
|
||||
XLogRecPtr unloggedLSN; /* current fake LSN value, for unlogged rels */
|
||||
|
||||
@@ -438,7 +377,7 @@ typedef struct ControlFileData12
|
||||
#endif
|
||||
|
||||
extern int get_pg_version(const char *data_directory, char *version_string);
|
||||
extern DBState get_db_state(const char *data_directory);
|
||||
extern bool get_db_state(const char *data_directory, DBState *state);
|
||||
extern const char *describe_db_state(DBState state);
|
||||
extern int get_data_checksum_version(const char *data_directory);
|
||||
extern uint64 get_system_identifier(const char *data_directory);
|
||||
|
||||
121
dbutils.h
121
dbutils.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* dbutils.h
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -60,11 +60,6 @@
|
||||
"NULL AS attached "
|
||||
|
||||
|
||||
|
||||
#define BDR2_NODES_COLUMNS "node_sysid, node_timeline, node_dboid, node_name, node_local_dsn, ''"
|
||||
#define BDR3_NODES_COLUMNS "ns.node_id, 0, 0, ns.node_name, ns.interface_connstr, ns.peer_state_name"
|
||||
|
||||
|
||||
#define ERRBUFF_SIZE 512
|
||||
|
||||
typedef enum
|
||||
@@ -72,8 +67,7 @@ typedef enum
|
||||
UNKNOWN = 0,
|
||||
PRIMARY,
|
||||
STANDBY,
|
||||
WITNESS,
|
||||
BDR
|
||||
WITNESS
|
||||
} t_server_type;
|
||||
|
||||
typedef enum
|
||||
@@ -125,14 +119,21 @@ typedef enum
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* unable to query "pg_stat_replication" or other error */
|
||||
NODE_ATTACHED_UNKNOWN = -1,
|
||||
NODE_DETACHED,
|
||||
NODE_ATTACHED
|
||||
/* node has record in "pg_stat_replication" and state is not "streaming" */
|
||||
NODE_ATTACHED,
|
||||
/* node has record in "pg_stat_replication" but state is not "streaming" */
|
||||
NODE_NOT_ATTACHED,
|
||||
/* node has no record in "pg_stat_replication" */
|
||||
NODE_DETACHED
|
||||
} NodeAttached;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SLOT_UNKNOWN = -1,
|
||||
SLOT_NOT_FOUND,
|
||||
SLOT_NOT_PHYSICAL,
|
||||
SLOT_INACTIVE,
|
||||
SLOT_ACTIVE
|
||||
} ReplSlotStatus;
|
||||
@@ -170,6 +171,7 @@ typedef struct
|
||||
char current_timestamp[MAXLEN];
|
||||
bool in_recovery;
|
||||
TimeLineID timeline_id;
|
||||
char timeline_id_str[MAXLEN];
|
||||
XLogRecPtr last_wal_receive_lsn;
|
||||
XLogRecPtr last_wal_replay_lsn;
|
||||
char last_xact_replay_timestamp[MAXLEN];
|
||||
@@ -324,45 +326,6 @@ typedef struct s_connection_user
|
||||
#define T_CONNECTION_USER_INITIALIZER { "", false }
|
||||
|
||||
|
||||
/* represents an entry in bdr.bdr_nodes */
|
||||
typedef struct s_bdr_node_info
|
||||
{
|
||||
char node_sysid[MAXLEN];
|
||||
uint32 node_timeline;
|
||||
uint32 node_dboid;
|
||||
char node_name[MAXLEN];
|
||||
char node_local_dsn[MAXLEN];
|
||||
char peer_state_name[MAXLEN];
|
||||
} t_bdr_node_info;
|
||||
|
||||
#define T_BDR_NODE_INFO_INITIALIZER { \
|
||||
"", InvalidOid, InvalidOid, \
|
||||
"", "", "" \
|
||||
}
|
||||
|
||||
|
||||
/* structs to store a list of BDR node records */
|
||||
typedef struct BdrNodeInfoListCell
|
||||
{
|
||||
struct BdrNodeInfoListCell *next;
|
||||
t_bdr_node_info *node_info;
|
||||
} BdrNodeInfoListCell;
|
||||
|
||||
typedef struct BdrNodeInfoList
|
||||
{
|
||||
BdrNodeInfoListCell *head;
|
||||
BdrNodeInfoListCell *tail;
|
||||
int node_count;
|
||||
} BdrNodeInfoList;
|
||||
|
||||
#define T_BDR_NODE_INFO_LIST_INITIALIZER { \
|
||||
NULL, \
|
||||
NULL, \
|
||||
0 \
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char filepath[MAXPGPATH];
|
||||
@@ -372,6 +335,7 @@ typedef struct
|
||||
|
||||
#define T_CONFIGFILE_INFO_INITIALIZER { "", "", false }
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int size;
|
||||
@@ -381,6 +345,7 @@ typedef struct
|
||||
|
||||
#define T_CONFIGFILE_LIST_INITIALIZER { 0, 0, NULL }
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64 system_identifier;
|
||||
@@ -420,10 +385,6 @@ typedef struct RepmgrdInfo {
|
||||
/* utility functions */
|
||||
|
||||
XLogRecPtr parse_lsn(const char *str);
|
||||
|
||||
extern void
|
||||
wrap_ddl_query(PQExpBufferData *query_buf, int replication_type, const char *fmt,...)
|
||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
|
||||
bool atobool(const char *value);
|
||||
|
||||
/* connection functions */
|
||||
@@ -432,13 +393,19 @@ PGconn *establish_db_connection(const char *conninfo,
|
||||
PGconn *establish_db_connection_quiet(const char *conninfo);
|
||||
PGconn *establish_db_connection_by_params(t_conninfo_param_list *param_list,
|
||||
const bool exit_on_error);
|
||||
PGconn *establish_db_connection_with_replacement_param(const char *conninfo,
|
||||
const char *param,
|
||||
const char *value,
|
||||
const bool exit_on_error);
|
||||
PGconn *establish_replication_connection_from_conn(PGconn *conn, const char *repluser);
|
||||
PGconn *establish_replication_connection_from_conninfo(const char *conninfo, const char *repluser);
|
||||
|
||||
PGconn *establish_primary_db_connection(PGconn *conn,
|
||||
const bool exit_on_error);
|
||||
PGconn *get_primary_connection(PGconn *standby_conn, int *primary_id, char *primary_conninfo_out);
|
||||
PGconn *get_primary_connection_quiet(PGconn *standby_conn, int *primary_id, char *primary_conninfo_out);
|
||||
PGconn *duplicate_connection(PGconn *conn, const char *user, bool replication);
|
||||
|
||||
bool is_superuser_connection(PGconn *conn, t_connection_user *userinfo);
|
||||
bool connection_has_pg_settings(PGconn *conn);
|
||||
void close_connection(PGconn **conn);
|
||||
|
||||
/* conninfo manipulation functions */
|
||||
@@ -451,6 +418,7 @@ void conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list);
|
||||
void param_set(t_conninfo_param_list *param_list, const char *param, const char *value);
|
||||
void param_set_ine(t_conninfo_param_list *param_list, const char *param, const char *value);
|
||||
char *param_get(t_conninfo_param_list *param_list, const char *param);
|
||||
bool validate_conninfo_string(const char *conninfo_str, char **errmsg);
|
||||
bool parse_conninfo_string(const char *conninfo_str, t_conninfo_param_list *param_list, char **errmsg, bool ignore_local_params);
|
||||
char *param_list_to_string(t_conninfo_param_list *param_list);
|
||||
char *normalize_conninfo_string(const char *conninfo_str);
|
||||
@@ -467,6 +435,7 @@ bool set_config(PGconn *conn, const char *config_param, const char *config_valu
|
||||
bool set_config_bool(PGconn *conn, const char *config_param, bool state);
|
||||
int guc_set(PGconn *conn, const char *parameter, const char *op, const char *value);
|
||||
bool get_pg_setting(PGconn *conn, const char *setting, char *output);
|
||||
bool get_pg_setting_bool(PGconn *conn, const char *setting, bool *output);
|
||||
bool get_pg_setting_int(PGconn *conn, const char *setting, int *output);
|
||||
bool alter_system_int(PGconn *conn, const char *name, int value);
|
||||
bool pg_reload_conf(PGconn *conn);
|
||||
@@ -482,6 +451,12 @@ bool identify_system(PGconn *repl_conn, t_system_identification *identification
|
||||
uint64 system_identifier(PGconn *conn);
|
||||
TimeLineHistoryEntry *get_timeline_history(PGconn *repl_conn, TimeLineID tli);
|
||||
|
||||
/* user/role information functions */
|
||||
bool can_execute_pg_promote(PGconn *conn);
|
||||
bool connection_has_pg_monitor_role(PGconn *conn, const char *subrole);
|
||||
bool is_replication_role(PGconn *conn, char *rolname);
|
||||
bool is_superuser_connection(PGconn *conn, t_connection_user *userinfo);
|
||||
|
||||
/* repmgrd shared memory functions */
|
||||
bool repmgrd_set_local_node_id(PGconn *conn, int local_node_id);
|
||||
int repmgrd_get_local_node_id(PGconn *conn);
|
||||
@@ -521,6 +496,7 @@ bool get_local_node_record(PGconn *conn, int node_id, t_node_info *node_info);
|
||||
bool get_primary_node_record(PGconn *conn, t_node_info *node_info);
|
||||
|
||||
bool get_all_node_records(PGconn *conn, NodeInfoList *node_list);
|
||||
bool get_all_nodes_count(PGconn *conn, int *count);
|
||||
void get_downstream_node_records(PGconn *conn, int node_id, NodeInfoList *nodes);
|
||||
void get_active_sibling_node_records(PGconn *conn, int node_id, int upstream_node_id, NodeInfoList *node_list);
|
||||
bool get_child_nodes(PGconn *conn, int node_id, NodeInfoList *node_list);
|
||||
@@ -559,10 +535,14 @@ PGresult *get_event_records(PGconn *conn, int node_id, const char *node_name,
|
||||
|
||||
/* replication slot functions */
|
||||
void create_slot_name(char *slot_name, int node_id);
|
||||
bool create_replication_slot(PGconn *conn, char *slot_name, PQExpBufferData *error_msg);
|
||||
bool drop_replication_slot(PGconn *conn, char *slot_name);
|
||||
|
||||
bool create_replication_slot_sql(PGconn *conn, char *slot_name, PQExpBufferData *error_msg);
|
||||
bool create_replication_slot_replprot(PGconn *conn, PGconn *repl_conn, char *slot_name, PQExpBufferData *error_msg);
|
||||
bool drop_replication_slot_sql(PGconn *conn, char *slot_name);
|
||||
bool drop_replication_slot_replprot(PGconn *repl_conn, char *slot_name);
|
||||
|
||||
RecordStatus get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
|
||||
int get_free_replication_slot_count(PGconn *conn);
|
||||
int get_free_replication_slot_count(PGconn *conn, int *max_replication_slots);
|
||||
int get_inactive_replication_slots(PGconn *conn, KeyValueList *list);
|
||||
|
||||
/* tablespace functions */
|
||||
@@ -614,35 +594,14 @@ XLogRecPtr get_last_wal_receive_location(PGconn *conn);
|
||||
void init_replication_info(ReplInfo *replication_info);
|
||||
bool get_replication_info(PGconn *conn, t_server_type node_type, ReplInfo *replication_info);
|
||||
int get_replication_lag_seconds(PGconn *conn);
|
||||
TimeLineID get_node_timeline(PGconn *conn);
|
||||
TimeLineID get_node_timeline(PGconn *conn, char *timeline_id_str);
|
||||
void get_node_replication_stats(PGconn *conn, t_node_info *node_info);
|
||||
NodeAttached is_downstream_node_attached(PGconn *conn, char *node_name);
|
||||
NodeAttached is_downstream_node_attached(PGconn *conn, char *node_name, char **node_state);
|
||||
void set_upstream_last_seen(PGconn *conn, int upstream_node_id);
|
||||
int get_upstream_last_seen(PGconn *conn, t_server_type node_type);
|
||||
|
||||
bool is_wal_replay_paused(PGconn *conn, bool check_pending_wal);
|
||||
|
||||
/* BDR functions */
|
||||
int get_bdr_version_num(void);
|
||||
void get_all_bdr_node_records(PGconn *conn, BdrNodeInfoList *node_list);
|
||||
RecordStatus get_bdr_node_record_by_name(PGconn *conn, const char *node_name, t_bdr_node_info *node_info);
|
||||
bool is_bdr_db(PGconn *conn, PQExpBufferData *output);
|
||||
bool is_bdr_db_quiet(PGconn *conn);
|
||||
bool is_active_bdr_node(PGconn *conn, const char *node_name);
|
||||
bool is_bdr_repmgr(PGconn *conn);
|
||||
char *get_default_bdr_replication_set(PGconn *conn);
|
||||
bool is_table_in_bdr_replication_set(PGconn *conn, const char *tablename, const char *set);
|
||||
bool add_table_to_bdr_replication_set(PGconn *conn, const char *tablename, const char *set);
|
||||
void add_extension_tables_to_bdr_replication_set(PGconn *conn);
|
||||
bool bdr_node_name_matches(PGconn *conn, const char *node_name, PQExpBufferData *bdr_local_node_name);
|
||||
ReplSlotStatus get_bdr_node_replication_slot_status(PGconn *conn, const char *node_name);
|
||||
void get_bdr_other_node_name(PGconn *conn, int node_id, char *name_buf);
|
||||
|
||||
bool am_bdr_failover_handler(PGconn *conn, int node_id);
|
||||
void unset_bdr_failover_handler(PGconn *conn);
|
||||
bool bdr_node_has_repmgr_set(PGconn *conn, const char *node_name);
|
||||
bool bdr_node_set_repmgr_set(PGconn *conn, const char *node_name);
|
||||
|
||||
/* miscellaneous debugging functions */
|
||||
const char *print_node_status(NodeStatus node_status);
|
||||
const char *print_pqping_status(PGPing ping_status);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* dirmod.c
|
||||
* directory handling functions
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* dirutil.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -95,6 +95,7 @@ clean:
|
||||
rm -f repmgr.html
|
||||
rm -f repmgr-A4.pdf
|
||||
rm -f repmgr-US.pdf
|
||||
rm -f html/*
|
||||
|
||||
maintainer-clean:
|
||||
rm -rf html
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
&repmgr; 5 is fundamentally the same code base as &repmgr; 4, but provides
|
||||
support for the revised replication configuration mechanism in PostgreSQL 12.
|
||||
</para>
|
||||
<para>
|
||||
Support for PostgreSQL 9.3 is no longer available from &repmgr; 5.2.
|
||||
</para>
|
||||
</note>
|
||||
<para>
|
||||
&repmgr; 3.x builds on the improved replication facilities added
|
||||
@@ -245,7 +248,7 @@
|
||||
<para>
|
||||
For a standby which has been manually cloned or recovered from an external
|
||||
backup manager such as Barman, the command
|
||||
<command><link linkend="repmgr-standby-clone">repmgr standby clone --recovery-conf-only</link></command>
|
||||
<command><link linkend="repmgr-standby-clone">repmgr standby clone --replication-conf-only</link></command>
|
||||
can be used to create the correct <filename>recovery.conf</filename> file for
|
||||
use with &repmgr; (and will create a replication slot if required). Once this has been done,
|
||||
<link linkend="repmgr-standby-register">register the node</link> as usual.
|
||||
@@ -347,6 +350,9 @@
|
||||
and earlier) with the absolute path to the WAL directory in <varname>pg_basebackup_options</varname>.
|
||||
For more details see <xref linkend="cloning-advanced-pg-basebackup-options"/>.
|
||||
</para>
|
||||
<para>
|
||||
In &repmgr; 5.2 and later, this setting will also be honoured when cloning from Barman.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="faq-repmgr-events-no-fkey" xreflabel="No foreign key on node_id in repmgr.events">
|
||||
@@ -373,6 +379,22 @@
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="faq-repmgr-exclude-metadata-from-dump" xreflabel="Excluding repmgr metadata from pg_dump output">
|
||||
<title>How can I exclude &repmgr; metadata from <application>pg_dump</application> output?</title>
|
||||
<para>
|
||||
Beginning with &repmgr; 5.2, the metadata tables associated with the &repmgr; extension
|
||||
(<literal>repmgr.nodes</literal>, <literal>repmgr.events</literal> and <literal>repmgr.monitoring_history</literal>)
|
||||
have been marked as dumpable as they contain configuration and user-generated data.
|
||||
</para>
|
||||
<para>
|
||||
To exclude these from <application>pg_dump</application> output, add the flag <option>--exclude-schema=repmgr</option>.
|
||||
</para>
|
||||
<para>
|
||||
To exclude individual &repmgr; metadata tables from <application>pg_dump</application> output, add the flag
|
||||
e.g. <option>--exclude-table=repmgr.monitoring_history</option>. This flag can be provided multiple times
|
||||
to exclude individual tables,
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
@@ -16,24 +16,395 @@
|
||||
</para>
|
||||
|
||||
<!-- remember to update the release date in ../repmgr_version.h.in -->
|
||||
<sect1 id="release-5.0.1">
|
||||
<title>Release 5.0.1</title>
|
||||
<para><emphasis>???</emphasis></para>
|
||||
|
||||
<sect1 id="release-5.2.0">
|
||||
<title id="release-current">Release 5.2.0</title>
|
||||
<para><emphasis>Thu 22 October, 2020</emphasis></para>
|
||||
|
||||
<para>
|
||||
&repmgr; 5.0.1 is a minor release.
|
||||
&repmgr; 5.2.0 is a major release.
|
||||
</para>
|
||||
<para>
|
||||
This release provides support for <ulink url="https://www.postgresql.org/docs/13/release-13.html">PostgreSQL 13</ulink>, released in September 2020.
|
||||
</para>
|
||||
<para>
|
||||
This release removes support for PostgreSQL 9.3, which was
|
||||
<ulink url="https://www.postgresql.org/docs/9.3/release-9-3-25.html">designated EOL in November 2018</ulink>.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>General improvements</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Configuration: support <command>include</command>, <command>include_dir</command> and
|
||||
<command>include_if_exists</command> directives (see <xref linkend="configuration-file-include-directives"/>).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
|
||||
Improve sanity check failure log output from the demotion candidate.
|
||||
</para>
|
||||
<para>
|
||||
If database connection configuration is not consistent across all nodes, it's
|
||||
possible remote &repmgr; invocations (e.g. during switchover, from the promotion candidate
|
||||
to the demotion candidate) will not be able to connect to the database. This will
|
||||
now be explicitly reported as a database connection failure, rather than as a failure
|
||||
of the respective sanity check.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-cluster-crosscheck">repmgr cluster crosscheck</link> /
|
||||
<link linkend="repmgr-cluster-matrix">repmgr cluster matrix</link>:
|
||||
improve text mode output format, in particular so that node identifiers of arbitrary length are
|
||||
displayed correctly.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-primary-unregister">repmgr primary unregister</link>:
|
||||
the <option>--force</option> can be provided to unregister an active primary node, provided
|
||||
it has no registered standby nodes.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-clone">repmgr standby clone</link>: new option
|
||||
<option>--verify-backup</option> to run PostgreSQL's
|
||||
<ulink url="https://www.postgresql.org/docs/13/app-pgverifybackup.html">pg_verifybackup</ulink>
|
||||
utility after cloning a standby to verify the integrity of the copied data
|
||||
(PostgreSQL 13 and later).
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
|
||||
when cloning from Barman, setting <option>--waldir</option>
|
||||
(PostgreSQL 9.6 and earlier: <option>--xlogdir</option>) in
|
||||
<option>pg_basebackup_options</option> will cause &repmgr; to create
|
||||
a WAL directory outside of the main data directory and symlink
|
||||
it from there, in the same way as would happen when cloning
|
||||
using <application>pg_basebackup</application>.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-follow">repmgr standby follow</link>:
|
||||
In PostgreSQL 13 and later, a standby no longer requires a restart to
|
||||
follow a new upstream node.
|
||||
</para>
|
||||
<para>
|
||||
The old behaviour (always restarting the standby to follow a new node)
|
||||
can be restored by setting the configuration file parameter
|
||||
<varname>standby_follow_restart</varname> to <literal>true</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-rejoin">repmgr node rejoin</link>:
|
||||
enable a node to attach to a target node even the target node
|
||||
has a lower timeline (PostgreSQL 9.6 and later).
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-rejoin">repmgr node rejoin</link>:
|
||||
in PostgreSQL 13 and later, support <application>pg_rewind</application>'s
|
||||
ability to automatically run crash recovery on a PostgreSQL instance
|
||||
which was not shut down cleanly.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-check">repmgr node check</link>:
|
||||
option <option>--db-connection</option> added to check if &repmgr;
|
||||
can connect to the database on the local node.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-check">repmgr node check</link>:
|
||||
report database connection error if the <option>--optformat</option> was provided.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Improve handling of pg_control read errors.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
It is now possible to dump the contents of &repmgr; metadata tables with
|
||||
<application>pg_dump</application>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>repmgrd enhancements</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Following additional parameters can be provided to <varname>failover_validation_command</varname>:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara><literal>%n</literal>: node ID</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%a</literal>: node name</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%v</literal>: number of visible nodes</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%u</literal>: number of shared upstream nodes</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%t</literal>: total number of nodes</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Configuration option <varname>always_promote</varname> (default: <literal>false</literal>)
|
||||
to control whether a node should be promoted if the &repmgr; metadata is not up-to-date
|
||||
on that node.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Bug fixes</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<command><link linkend="repmgr-standby-follow">repmgr standby follow</link></command>:
|
||||
ensure an existing replication slot is not deleted if the
|
||||
follow target is the node's current upstream.
|
||||
<link linkend="repmgr-standby-clone">repmgr standby clone</link>:
|
||||
fix issue with cloning from Barman where the tablespace mapping file was
|
||||
not flushed to disk before attempting to retrieve files from Barman. GitHub #650.
|
||||
</para>
|
||||
</listitem>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-rejoin">repmgr node rejoin</link>:
|
||||
ensure that when verifying a standby node has attached to its upstream, the
|
||||
node has started streaming before confirming the success of the rejoin operation.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
&repmgrd;: ensure primary connection is reset if same as upstream. GitHub #633.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.1.0">
|
||||
<title>Release 5.1.0</title>
|
||||
<para><emphasis>Mon 13 April, 2020</emphasis></para>
|
||||
|
||||
<para>
|
||||
&repmgr; 5.1.0 is a major release.
|
||||
</para>
|
||||
<para>
|
||||
For details on how to upgrade an existing &repmgr; installation, see
|
||||
documentation section <link linkend="upgrading-major-version">Upgrading a major version release</link>.
|
||||
</para>
|
||||
<para>
|
||||
If &repmgrd; is in use, a PostgreSQL restart <emphasis>is</emphasis> required;
|
||||
in that case we suggest combining this &repmgr; upgrade with the next PostgreSQL
|
||||
minor release, which will require a PostgreSQL restart in any case.
|
||||
</para>
|
||||
|
||||
|
||||
<sect2>
|
||||
<title>Compatibility changes</title>
|
||||
<para>
|
||||
The <link linkend="repmgr-standby-clone"><command>repmgr standby clone</command></link>
|
||||
<option>--recovery-conf-only</option> option has been renamed to
|
||||
<option>--replication-conf-only</option>. <option>--recovery-conf-only</option> will
|
||||
still be accepted as an alias.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>General improvements</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The requirement that the &repmgr; user is a database superuser has been
|
||||
removed as far as possible.
|
||||
</para>
|
||||
<para>
|
||||
In theory, &repmgr; can be operated with a normal database user for managing
|
||||
the &repmgr; database, and a separate replication user for managing replication
|
||||
connections (and replication slots, if these are in use).
|
||||
</para>
|
||||
<para>
|
||||
Some operations will still require superuser permissions, e.g. for issuing
|
||||
a <command>CHECKPOINT</command> as par of a switchover operation; in this case
|
||||
a valid superuser should be provided with the <option>-S</option>/<option>--superuser</option>
|
||||
option.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-clone"><command>repmgr standby clone</command></link>:
|
||||
Warn if neither of data page checksums or <option>wal_log_hints</option> are active,
|
||||
as this will preclude later usage of <command>pg_rewind</command>.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>:
|
||||
when executed with <option>--dry-run</option>, the method which would be used to promote the node
|
||||
will be displayed.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link>:
|
||||
Improve logging and checking of potential failure situations.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
|
||||
Replication configuration files (PostgreSQL 11 and earlier:
|
||||
<filename>recovery.conf</filename>; PostgreSQL 12 and later: <filename>postgresql.auto.conf</filename>)
|
||||
will be checked to ensure they are owned by the same user who owns the PostgreSQL
|
||||
data directory.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
|
||||
Provide additional information in <option>--dry-run mode</option> output.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
|
||||
Checks that the demotion candidate's registered repmgr.conf file can be found, to
|
||||
prevent confusing references to an incorrectly configured data directory. GitHub 615.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-check"><command>repmgr node check</command></link>:
|
||||
accept option <option>-S</option>/<option>--superuser</option>. GitHub #621.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-node-check"><command>repmgr node check</command></link>:
|
||||
add <option>--upstream</option> option to check whether the node is attached
|
||||
to the expected upstream node.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Bug fixes</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Ensure <link linkend="repmgr-node-rejoin"><command>repmgr node rejoin</command></link>
|
||||
checks for available replication slots on the rejoin target.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link> and
|
||||
<link linkend="repmgr-node-rejoin"><command>repmgr node rejoin</command></link> will now return
|
||||
an error code if the operation fails if a replication slot is not available or cannot
|
||||
be created on the follow/rejoin target.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>:
|
||||
in <option>--dry-run mode</option>, display promote command which will be executed.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>
|
||||
will check if the <literal>repmgr</literal> user has permission to execute
|
||||
<function>pg_promote()</function> and fall back to <command>pg_ctl promote</command> if
|
||||
necessary.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<link linkend="repmgr-standby-switchover"><command>repmgr standby switchover</command></link>:
|
||||
check for demotion candidate reattachment as late as possible to avoid spurious failure
|
||||
reports.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
&repmgrd;: check for presence of <option>promote_command</option> and
|
||||
<option>follow_command</option> on receipt of <literal>SIGHUP</literal>. GitHub 614.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Fix situation where replication connections were not created correctly, which
|
||||
could lead to spurious replication connection failures in some situations, e.g.
|
||||
where password files are used.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Ensure <filename>postgresql.auto.conf</filename> is created with
|
||||
correct permissions (PostgreSQL 12 and later).
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
@@ -41,9 +412,8 @@
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="release-5.0">
|
||||
<title id="release-current">Release 5.0</title>
|
||||
<title>Release 5.0</title>
|
||||
<para><emphasis>Tue 15 October, 2019</emphasis></para>
|
||||
|
||||
<para>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
BDR failover with repmgrd
|
||||
=========================
|
||||
|
||||
This document has been integrated into the main `repmgr` documentation
|
||||
and is now located here:
|
||||
|
||||
> [BDR failover with repmgrd](https://repmgr.org/docs/current/repmgrd-bdr.html)
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<para>
|
||||
WAL management on the primary becomes much easier as there's no need
|
||||
to use replication slots, and <varname>wal_keep_segments</varname>
|
||||
(PostgreSQL 13 and later: <varname>wal_keep_size</varname>)
|
||||
does not need to be set.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -64,8 +65,9 @@
|
||||
</para>
|
||||
<para>
|
||||
Barman's parallel restore facility can be used by executing it manually on
|
||||
the Barman server and integrating the resulting cloned standby using
|
||||
<command><link linkend="repmgr-standby-clone">repmgr standby clone --recovery-conf-only</link></command>.
|
||||
the Barman server and configuring replication on the resulting cloned
|
||||
standby using
|
||||
<command><link linkend="repmgr-standby-clone">repmgr standby clone --replication-conf-only</link></command>.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
@@ -146,6 +148,15 @@ description = "Main cluster"
|
||||
section in <command>man 5 ssh_config</command> for more details.
|
||||
</simpara>
|
||||
</tip>
|
||||
<para>
|
||||
If you wish to place WAL files in a location outside the main
|
||||
PostgreSQL data directory, set <option>--waldir</option>
|
||||
(PostgreSQL 9.6 and earlier: <option>--xlogdir</option>) in
|
||||
<option>pg_basebackup_options</option> to the target directory
|
||||
(must be an absolute filepath). &repmgr; will create and
|
||||
symlink to this directory in exactly the same way
|
||||
<application>pg_basebackup</application> would.
|
||||
</para>
|
||||
<para>
|
||||
It's now possible to clone a standby from Barman, e.g.:
|
||||
<programlisting>
|
||||
@@ -233,7 +244,8 @@ description = "Main cluster"
|
||||
that any standby connected to the primary using a replication slot will always
|
||||
be able to retrieve the required WAL files. This removes the need to manually
|
||||
manage WAL file retention by estimating the number of WAL files that need to
|
||||
be maintained on the primary using <varname>wal_keep_segments</varname>.
|
||||
be maintained on the primary using <varname>wal_keep_segments</varname>
|
||||
(PostgreSQL 13 and later: <varname>wal_keep_size</varname>).
|
||||
Do however be aware that if a standby is disconnected, WAL will continue to
|
||||
accumulate on the primary until either the standby reconnects or the replication
|
||||
slot is dropped.
|
||||
@@ -437,6 +449,13 @@ description = "Main cluster"
|
||||
WAL directory. Any WALs generated during the cloning process will be copied here, and
|
||||
a symlink will automatically be created from the main data directory.
|
||||
</para>
|
||||
<tip>
|
||||
<para>
|
||||
The <literal>--waldir</literal> (<literal>--xlogdir</literal>) option,
|
||||
if present in <varname>pg_basebackup_options</varname>, will be honoured by &repmgr;
|
||||
when cloning from Barman (&repmgr; 5.2 and later).
|
||||
</para>
|
||||
</tip>
|
||||
<para>
|
||||
See the <ulink url="https://www.postgresql.org/docs/current/app-pgbasebackup.html">PostgreSQL pg_basebackup documentation</ulink>
|
||||
for more details of available options.
|
||||
|
||||
@@ -66,17 +66,8 @@
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Must be one of <literal>physical</literal> (for standard streaming replication)
|
||||
or <literal>bdr</literal>.
|
||||
Must be <literal>physical</literal> (the default).
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
Replication type <literal>bdr</literal> can only be used with BDR 2.x
|
||||
</para>
|
||||
<para>
|
||||
BDR 3.x users should use <literal>physical</literal>.
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -80,6 +80,51 @@ conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlistin
|
||||
</para>
|
||||
</note>
|
||||
|
||||
|
||||
<sect3 id="configuration-file-include-directives" xreflabel="configuration file include directives">
|
||||
|
||||
<title>Configuration file include directives</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>repmgr.conf</primary>
|
||||
<secondary>include directives</secondary>
|
||||
</indexterm>
|
||||
<para>
|
||||
From &repmgr; 5.2, the configuration file can contain the following include directives:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>include</option>: include the specified file,
|
||||
either as an absolute path or path relative to the current file
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>include_if_exists</option>: include the specified file.
|
||||
The file is specified as an absolute path or path relative to the current file.
|
||||
However, if it does not exist, an error will not be raised.
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>include_dir</option>: include files in the specified directory
|
||||
which have the <filename>.conf</filename> suffix.
|
||||
The directory is specified either as an absolute path or path
|
||||
relative to the current file
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
These behave in exactly the same way as the PostgreSQL configuration file processing;
|
||||
see the <ulink url="https://www.postgresql.org/docs/current/config-setting.html#CONFIG-INCLUDES">PostgreSQL documentation</ulink>
|
||||
for additional details.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
@@ -182,6 +227,14 @@ conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlistin
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In examples provided in this documentation, it is assumed the configuration file is located
|
||||
at <filename>/etc/repmgr.conf</filename>. If &repmgr; is installed from a package, the
|
||||
configuration file will probably be located at another location specified by the packager;
|
||||
see appendix <xref linkend="appendix-packages"/> for configuration file locations in
|
||||
different packaging systems.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that if a file is explicitly specified with <literal>-f/--config-file</literal>,
|
||||
an error will be raised if it is not found or not readable, and no attempt will be made to
|
||||
@@ -202,6 +255,61 @@ conninfo='host=node1 user=repmgr dbname=repmgr connect_timeout=2'</programlistin
|
||||
<filename>/path/to/repmgr.conf</filename>).
|
||||
</para>
|
||||
</note>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="configuration-file-postgresql-major-upgrades" xreflabel="configuration file and PostgreSQL major version upgrades">
|
||||
<title>Configuration file and PostgreSQL major version upgrades</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>repmgr.conf</primary>
|
||||
<secondary>ostgreSQL major version upgrades</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
When upgrading the PostgreSQL cluster to a new major version, <filename>repmgr.conf</filename>
|
||||
will probably needed to be updated.
|
||||
</para>
|
||||
<para>
|
||||
Usually <option>pg_bindir</option> and <option>data_directory</option> will need to be modified,
|
||||
particularly if the default package locations are used, as these usually change.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It's also possible the location of <filename>repmgr.conf</filename> itself will change
|
||||
(e.g. from <filename>/etc/repmgr/11/repmgr.conf</filename> to <filename>/etc/repmgr/12/repmgr.conf</filename>).
|
||||
This is stored as part of the &repmgr; metadata and is used by &repmgr; to execute &repmgr; remotely
|
||||
(e.g. during a <link linkend="performing-switchover">switchover operation</link>).
|
||||
</para>
|
||||
<para>
|
||||
If the content and/or location of <filename>repmgr.conf</filename> has changed, the &repmgr; metadata
|
||||
needs to be updated to reflect this. The &repmgr; metadata can be updated on each node with:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara>
|
||||
<link linkend="repmgr-primary-register">
|
||||
<command>repmgr primary register --force -f /path/to/repmgr.conf</command>
|
||||
</link>
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<link linkend="repmgr-standby-register">
|
||||
<command>repmgr standby register --force -f /path/to/repmgr.conf</command>
|
||||
</link>
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<link linkend="repmgr-witness-register">
|
||||
<command>repmgr witness register --force -f /path/to/repmgr.conf -h primary_host</command>
|
||||
</link>
|
||||
</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
@@ -127,8 +127,31 @@ node2:5432:repmgr:repmgr:foo
|
||||
node2:5432:replication:repluser:foo
|
||||
node3:5432:repmgr:repmgr:foo
|
||||
node3:5432:replication:repluser:foo</programlisting>
|
||||
|
||||
If you are planning to use the <option>-S</option>/<option>--superuser</option> option,
|
||||
there must also be an entry enabling the superuser to connect to the &repmgr; database.
|
||||
Assuming the superuser is <literal>postgres</literal>, the file would look like this:
|
||||
<programlisting>
|
||||
node1:5432:repmgr:repmgr:foo
|
||||
node1:5432:repmgr:postgres:foo
|
||||
node1:5432:replication:repluser:foo
|
||||
node2:5432:repmgr:repmgr:foo
|
||||
node2:5432:repmgr:postgres:foo
|
||||
node2:5432:replication:repluser:foo
|
||||
node3:5432:repmgr:repmgr:foo
|
||||
node3:5432:repmgr:postgres:foo
|
||||
node3:5432:replication:repluser:foo</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <filename>~/.pgpass</filename> file can be simplified with the use of wildcards if
|
||||
there is no requirement to restrict provision of passwords to particular hosts, ports
|
||||
or databases. The preceding file could then be formatted like this:
|
||||
<programlisting>
|
||||
*:*:*:repmgr:foo
|
||||
*:*:*:postgres:foo
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
It's possible to specify an alternative location for the <filename>~/.pgpass</filename> file, either via
|
||||
@@ -140,6 +163,11 @@ node3:5432:replication:repluser:foo</programlisting>
|
||||
location on all nodes, as when connecting to a remote node, the file referenced is the one on the
|
||||
local node.
|
||||
</para>
|
||||
<para>
|
||||
Additionally, you <emphasis>must</emphasis> specify the passfile location in <filename>repmgr.conf</filename>
|
||||
with the <option>passfile</option> option so &repmgr; can write the correct path when creating the
|
||||
<option>primary_conninfo</option> parameter for replication configuration on standbys.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
</sect2>
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
|
||||
<varlistentry>
|
||||
|
||||
<term><option>wal_keep_segments</option></term>
|
||||
<term><option>wal_keep_segments</option> / <option>wal_keep_size</option></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
@@ -279,25 +279,36 @@
|
||||
<secondary>PostgreSQL configuration</secondary>
|
||||
</indexterm>
|
||||
|
||||
|
||||
<indexterm>
|
||||
<primary>wal_keep_size</primary>
|
||||
<secondary>PostgreSQL configuration</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
Normally there is no need to set <option>wal_keep_segments</option> (default: <literal>0</literal>), as it
|
||||
is <emphasis>not</emphasis> a reliable way of ensuring that all required WAL segments are available to standbys.
|
||||
Replication slots and/or an archiving solution such as Barman are recommended to ensure standbys have a reliable
|
||||
Normally there is no need to set <option>wal_keep_segments</option>
|
||||
(PostgreSQL 13 and later: <varname>wal_keep_size</varname>; default: <literal>0</literal>),
|
||||
as it is <emphasis>not</emphasis> a reliable way of ensuring that all required WAL
|
||||
segments are available to standbys. Replication slots and/or an archiving solution
|
||||
such as Barman are recommended to ensure standbys have a reliable
|
||||
source of WAL segments at all times.
|
||||
</para>
|
||||
<para>
|
||||
The only reason ever to set <option>wal_keep_segments</option> is you have
|
||||
you have configured <option>pg_basebackup_options</option>
|
||||
The only reason ever to set <option>wal_keep_segments</option> / <option>wal_keep_size</option>
|
||||
is you have you have configured <option>pg_basebackup_options</option>
|
||||
in <filename>repmgr.conf</filename> to include the setting <literal>--wal-method=fetch</literal>
|
||||
(PostgreSQL 9.6 and earlier: <literal>--xlog-method=fetch</literal>)
|
||||
<emphasis>and</emphasis> you have <emphasis>not</emphasis> set <option>restore_command</option>
|
||||
in <filename>repmgr.conf</filename> to fetch WAL files from a reliable source such as Barman,
|
||||
in which case you'll need to set <option>wal_keep_segments</option>
|
||||
to a sufficiently high number to ensure that all WAL files required by the standby
|
||||
are retained. However we do not recommend managing replication in this way.
|
||||
are retained. However we do not recommend WAL retention in this way.
|
||||
</para>
|
||||
<para>
|
||||
PostgreSQL documentation: <ulink url="https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-WAL-KEEP-SEGMENTS">wal_keep_segments</ulink>.
|
||||
<!--
|
||||
PostgreSQL documentation: <ulink url="https://www.postgresql.org/docs/current/runtime-config-replication.html#GUC-WAL-KEEP-SIZE">wal_keep_size</ulink>.
|
||||
-->
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -117,10 +117,6 @@
|
||||
<literal>conninfo</literal> string of the primary node
|
||||
(<xref linkend="repmgr-standby-register"/> and <xref linkend="repmgr-standby-follow"/>)
|
||||
</para>
|
||||
<para>
|
||||
<literal>conninfo</literal> string of the next available node
|
||||
(<varname>bdr_failover</varname> and <varname>bdr_recovery</varname>)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -130,9 +126,6 @@
|
||||
<para>
|
||||
name of the current primary node (<xref linkend="repmgr-standby-register"/> and <xref linkend="repmgr-standby-follow"/>)
|
||||
</para>
|
||||
<para>
|
||||
name of the next available node (<varname>bdr_failover</varname> and <varname>bdr_recovery</varname>)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -273,28 +266,6 @@
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Events generated by &repmgrd; (BDR mode):
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara><literal>bdr_failover</literal></simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>bdr_reconnect</literal></simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>bdr_recovery</literal></simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>bdr_register</literal></simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>bdr_unregister</literal></simpara>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that under some circumstances (e.g. when no replication cluster primary
|
||||
could be located), it will not be possible to write an entry into the
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
<!ENTITY repmgrd-automatic-failover SYSTEM "repmgrd-automatic-failover.xml">
|
||||
<!ENTITY repmgrd-configuration SYSTEM "repmgrd-configuration.xml">
|
||||
<!ENTITY repmgrd-operation SYSTEM "repmgrd-operation.xml">
|
||||
<!ENTITY repmgrd-bdr SYSTEM "repmgrd-bdr.xml">
|
||||
|
||||
<!ENTITY repmgr-primary-register SYSTEM "repmgr-primary-register.xml">
|
||||
<!ENTITY repmgr-primary-unregister SYSTEM "repmgr-primary-unregister.xml">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 9.3. See
|
||||
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 9.4. See
|
||||
section <link linkend="install-compatibility-matrix">&repmgr; compatibility matrix</link>
|
||||
for an overview of version compatibility.
|
||||
</para>
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
&repmgr; 5.x
|
||||
&repmgr; 5.2
|
||||
</entry>
|
||||
<entry>
|
||||
YES
|
||||
@@ -123,11 +123,42 @@
|
||||
<entry>
|
||||
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
|
||||
</entry>
|
||||
<entry>
|
||||
9.4, 9.5, 9.6, 10, 11, 12, 13
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
&repmgr; 5.1
|
||||
</entry>
|
||||
<entry>
|
||||
NO
|
||||
</entry>
|
||||
<entry>
|
||||
<link linkend="release-5.1.0">5.1.0</link> (2020-04-13)
|
||||
</entry>
|
||||
<entry>
|
||||
9.3, 9.4, 9.5, 9.6, 10, 11, 12
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
&repmgr; 5.0
|
||||
</entry>
|
||||
<entry>
|
||||
NO
|
||||
</entry>
|
||||
<entry>
|
||||
<link linkend="release-5.0">5.0</link> (2019-10-15)
|
||||
</entry>
|
||||
<entry>
|
||||
9.3, 9.4, 9.5, 9.6, 10, 11, 12
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
&repmgr; 4.x
|
||||
@@ -193,52 +224,49 @@
|
||||
|
||||
<sect2 id="install-postgresql-93-94">
|
||||
|
||||
<title>PostgreSQL 9.3 and 9.4 support</title>
|
||||
<title>PostgreSQL 9.4 support</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>PostgreSQL 9.3</primary>
|
||||
<primary>PostgreSQL 9.4</primary>
|
||||
<secondary>repmgr support</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
Note that some &repmgr; functionality is not available in PostgreSQL 9.3 and PostgreSQL 9.4:
|
||||
Note that some &repmgr; functionality is not available in PostgreSQL 9.4:
|
||||
</para>
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
PostgreSQL 9.3 does not support replication slots, so corresponding &repmgr; functionality
|
||||
is not available.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
In PostgreSQL 9.3 and PostgreSQL 9.4, <command>pg_rewind</command> is not part of the core
|
||||
In PostgreSQL 9.4, <command>pg_rewind</command> is not part of the core
|
||||
distribution. <command>pg_rewind</command> will need to be compiled separately to be able
|
||||
to use any &repmgr; functionality which takes advantage of it.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<important>
|
||||
<warning>
|
||||
<para>
|
||||
PostgreSQL 9.3 has reached the end of its community support period (final release was
|
||||
<ulink url="https://www.postgresql.org/docs/9.3/release-9-3-25.html">9.3.25</ulink>
|
||||
in November 2018) and will no longer be updated with security or bugfixes.
|
||||
</para>
|
||||
<para>
|
||||
Beginning with &repmgr; 5.2, &repmgr; no longer supports PostgreSQL 9.3.
|
||||
</para>
|
||||
<para>
|
||||
PostgreSQL 9.4 has reached the end of its community support period (final release was
|
||||
<ulink url="https://www.postgresql.org/docs/9.4/release-9-4-26.html">9.4.26</ulink>
|
||||
in February 2020) and will no longer be updated with security or bugfixes.
|
||||
</para>
|
||||
<para>
|
||||
We recommend that users of these versions migrate to a recent PostgreSQL version
|
||||
We recommend that users of these versions migrate to a supported PostgreSQL version
|
||||
as soon as possible.
|
||||
</para>
|
||||
<para>
|
||||
For further details, see the <ulink url="https://www.postgresql.org/support/versioning/">PostgreSQL Versioning Policy</ulink>.
|
||||
</para>
|
||||
</important>
|
||||
</warning>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<date>2017</date>
|
||||
|
||||
<copyright>
|
||||
<year>2010-2019</year>
|
||||
<year>2010-2020</year>
|
||||
<holder>2ndQuadrant, Ltd.</holder>
|
||||
</copyright>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<title>Legal Notice</title>
|
||||
|
||||
<para>
|
||||
<productname>repmgr</productname> is Copyright © 2010-2019
|
||||
<productname>repmgr</productname> is Copyright © 2010-2020
|
||||
by 2ndQuadrant, Ltd. All rights reserved.
|
||||
</para>
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
For the sake of simplicity, the <literal>repmgr</literal> user is created
|
||||
as a superuser. If desired, it's possible to create the <literal>repmgr</literal>
|
||||
user as a normal user. However for certain operations superuser permissions
|
||||
are requiredl; in this case the command line option <command>--superuser</command>
|
||||
are required; in this case the command line option <command>--superuser</command>
|
||||
can be provided to specify a superuser.
|
||||
</para>
|
||||
<para>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<para>
|
||||
Displays information about each registered node in the replication cluster. This
|
||||
command polls each registered server and shows its role (<literal>primary</literal> /
|
||||
<literal>standby</literal> / <literal>bdr</literal>) and status. It polls each server
|
||||
<literal>standby</literal>) and status. It polls each server
|
||||
directly and can be run on any node in the cluster; this is also useful when analyzing
|
||||
connectivity from a particular node.
|
||||
</para>
|
||||
@@ -53,12 +53,13 @@
|
||||
<para>
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf cluster show
|
||||
|
||||
ID | Name | Role | Status | Upstream | Location | Priority | Timeline | Connection string
|
||||
----+-------+---------+-----------+----------+----------+----------+-----------------------------------------
|
||||
----+-------+---------+-----------+----------+----------+----------+----------+-----------------------------------------
|
||||
1 | node1 | primary | * running | | default | 100 | 1 | host=db_node1 dbname=repmgr user=repmgr
|
||||
2 | node2 | standby | running | node1 | default | 100 | 1 | host=db_node2 dbname=repmgr user=repmgr
|
||||
3 | node3 | standby | running | node1 | default | 100 | 1 | host=db_node3 dbname=repmgr user=repmgr</programlisting>
|
||||
3 | node3 | standby | running | node1 | default | 100 | 1 | host=db_node3 dbname=repmgr user=repmgr
|
||||
4 | node4 | standby | running | node1 | default | 100 | 1 | host=db_node4 dbname=repmgr user=repmgr
|
||||
5 | node5 | witness | * running | node1 | default | 0 | n/a | host=db_node5 dbname=repmgr user=repmgr</programlisting>
|
||||
</para>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
@@ -82,18 +83,22 @@
|
||||
(but <literal>node3</literal> is not attached to it, and its metadata has not yet been updated);
|
||||
<literal>node4</literal> is running but rejecting connections (from <literal>node3</literal> at least).
|
||||
<programlisting>
|
||||
ID | Name | Role | Status | Upstream | Location | Priority | Connection string
|
||||
----+-------+---------+----------------------+----------+----------+----------+-----------------------------------------
|
||||
1 | node1 | primary | ? unreachable | | default | 100 | host=db_node1 dbname=repmgr user=repmgr
|
||||
2 | node2 | standby | ! running as primary | node1 | default | 100 | host=db_node2 dbname=repmgr user=repmgr
|
||||
3 | node3 | standby | running | node1 | default | 100 | host=db_node3 dbname=repmgr user=repmgr
|
||||
4 | node4 | standby | ? running | node1 | default | 100 | host=db_node4 dbname=repmgr user=repmgr
|
||||
ID | Name | Role | Status | Upstream | Location | Priority | Timeline | Connection string
|
||||
----+-------+---------+----------------------+----------+----------+----------+----------+----------------------------------------------------
|
||||
1 | node1 | primary | ? unreachable | | default | 100 | | host=db_node1 dbname=repmgr user=repmgr
|
||||
2 | node2 | standby | ! running as primary | ? node1 | default | 100 | 2 | host=db_node2 dbname=repmgr user=repmgr
|
||||
3 | node3 | standby | running | ? node1 | default | 100 | 1 | host=db_node3 dbname=repmgr user=repmgr
|
||||
4 | node4 | standby | ? running | ? node1 | default | 100 | | host=db_node4 dbname=repmgr user=repmgr
|
||||
|
||||
WARNING: following issues were detected
|
||||
- unable to connect to node "node1" (ID: 1)
|
||||
- node "node1" (ID: 1) is registered as an active primary but is unreachable
|
||||
- node "node2" (ID: 2) is registered as standby but running as primary
|
||||
- unable to connect to node "node4" (ID: 4)
|
||||
WARNING: following issues were detected
|
||||
- unable to connect to node "node1" (ID: 1)
|
||||
- node "node1" (ID: 1) is registered as an active primary but is unreachable
|
||||
- node "node2" (ID: 2) is registered as standby but running as primary
|
||||
- unable to connect to node "node2" (ID: 2)'s upstream node "node1" (ID: 1)
|
||||
- unable to determine if node "node2" (ID: 2) is attached to its upstream node "node1" (ID: 1)
|
||||
- unable to connect to node "node3" (ID: 3)'s upstream node "node1" (ID: 1)
|
||||
- unable to determine if node "node3" (ID: 3) is attached to its upstream node "node1" (ID: 1)
|
||||
- unable to connect to node "node4" (ID: 4)
|
||||
HINT: execute with --verbose option to see connection error messages</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
|
||||
@@ -31,15 +31,32 @@
|
||||
<refsect1>
|
||||
<title>Example</title>
|
||||
<para>
|
||||
Execution on the primary server:
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf node check
|
||||
Node "node1":
|
||||
Server role: OK (node is primary)
|
||||
Replication lag: OK (N/A - node is primary)
|
||||
WAL archiving: OK (0 pending files)
|
||||
Upstream connection: OK (N/A - is primary)
|
||||
Downstream servers: OK (2 of 2 downstream nodes attached)
|
||||
Replication slots: OK (node has no physical replication slots)
|
||||
Missing replication slots: OK (node has no missing physical replication slots)</programlisting>
|
||||
Missing replication slots: OK (node has no missing physical replication slots)
|
||||
Configured data directory: OK (configured "data_directory" is "/var/lib/postgresql/data")</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Execution on a standby server:
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf node check
|
||||
Node "node2":
|
||||
Server role: OK (node is standby)
|
||||
Replication lag: OK (0 seconds)
|
||||
WAL archiving: OK (0 pending archive ready files)
|
||||
Upstream connection: OK (node "node2" (ID: 2) is attached to expected upstream node "node1" (ID: 1))
|
||||
Downstream servers: OK (this node has no downstream nodes)
|
||||
Replication slots: OK (node has no physical replication slots)
|
||||
Missing physical replication slots: OK (node has no missing physical replication slots)
|
||||
Configured data directory: OK (configured "data_directory" is "/var/lib/postgresql/data")</programlisting>
|
||||
</para>
|
||||
</refsect1>
|
||||
<refsect1>
|
||||
@@ -57,20 +74,20 @@
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--role</literal>: checks if the node has the expected role
|
||||
<option>--role</option>: checks if the node has the expected role
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--replication-lag</literal>: checks if the node is lagging by more than
|
||||
<option>--replication-lag</option>: checks if the node is lagging by more than
|
||||
<varname>replication_lag_warning</varname> or <varname>replication_lag_critical</varname>
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--archive-ready</literal>: checks for WAL files which have not yet been archived,
|
||||
<option>--archive-ready</option>: checks for WAL files which have not yet been archived,
|
||||
and returns <literal>WARNING</literal> or <literal>CRITICAL</literal> if the number
|
||||
exceeds <varname>archive_ready_warning</varname> or <varname>archive_ready_critical</varname> respectively.
|
||||
</simpara>
|
||||
@@ -78,25 +95,31 @@
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--downstream</literal>: checks that the expected downstream nodes are attached
|
||||
<option>--downstream</option>: checks that the expected downstream nodes are attached
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--slots</literal>: checks there are no inactive physical replication slots
|
||||
<option>--upstream</option>: checks that the node is attached to its expected upstream
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--missing-slots</literal>: checks there are no missing physical replication slots
|
||||
<option>--slots</option>: checks there are no inactive physical replication slots
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--data-directory-config</literal>: checks the data directory configured in
|
||||
<option>--missing-slots</option>: checks there are no missing physical replication slots
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>--data-directory-config</option>: checks the data directory configured in
|
||||
<filename>repmgr.conf</filename> matches the actual data directory.
|
||||
This check is not directly related to replication, but is useful to verify &repmgr;
|
||||
is correctly configured.
|
||||
@@ -108,6 +131,62 @@
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Additional checks</title>
|
||||
<para>
|
||||
Several checks are provided for diagnostic purposes and are not
|
||||
included in the general output:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>--db-connection</option>: checks if &repmgr; can connect to the
|
||||
database on the local node.
|
||||
</simpara>
|
||||
<simpara>
|
||||
This option is particularly useful in combination with <command>SSH</command>, as
|
||||
it can be used to troubleshoot connection issues encountered when &repmgr; is
|
||||
executed remotely (e.g. during a switchover operation).
|
||||
</simpara>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>--replication-config-owner</option>: checks if the file containing replication
|
||||
configuration (PostgreSQL 12 and later: <filename>postgresql.auto.conf</filename>;
|
||||
PostgreSQL 11 and earlier: <filename>recovery.conf</filename>) is
|
||||
owned by the same user who owns the data directory.
|
||||
</simpara>
|
||||
<simpara>
|
||||
Incorrect ownership of these files (e.g. if they are owned by <literal>root</literal>)
|
||||
will cause operations which need to update the replication configuration
|
||||
(e.g. <link linkend="repmgr-standby-follow"><command>repmgr standby follow</command></link>
|
||||
or <link linkend="repmgr-standby-promote"><command>repmgr standby promote</command></link>)
|
||||
to fail.
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Connection options</title>
|
||||
<para>
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<option>-S</option>/<option>--superuser</option>: connect as the
|
||||
named superuser instead of the &repmgr; user
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Output format</title>
|
||||
<para>
|
||||
@@ -115,14 +194,14 @@
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--csv</literal>: generate output in CSV format (not available
|
||||
<option>--csv</option>: generate output in CSV format (not available
|
||||
for individual checks)
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<literal>--nagios</literal>: generate output in a Nagios-compatible format
|
||||
<option>--nagios</option>: generate output in a Nagios-compatible format
|
||||
(for individual checks only)
|
||||
</simpara>
|
||||
</listitem>
|
||||
@@ -130,13 +209,15 @@
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>Exit codes</title>
|
||||
|
||||
<para>
|
||||
When executing <command>repmgr node check</command> with one of the individual
|
||||
checks listed above, &repmgr; will emit one of the following Nagios-style exit codes
|
||||
(even if <literal>--nagios</literal> is not supplied):
|
||||
(even if <option>--nagios</option> is not supplied):
|
||||
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
|
||||
|
||||
@@ -43,7 +43,12 @@
|
||||
<programlisting>
|
||||
repmgr node rejoin -d '$conninfo'</programlisting>
|
||||
|
||||
where <literal>$conninfo</literal> is the conninfo string of any reachable node in the cluster.
|
||||
where <literal>$conninfo</literal> is the PostgreSQL <literal>conninfo</literal> string of the
|
||||
<emphasis>current</emphasis> primary node (or that of any reachable node in the cluster, but
|
||||
<emphasis>not</emphasis> the local node). This is so that &repmgr; can fetch up-to-date information
|
||||
about the current state of the cluster.
|
||||
</para>
|
||||
<para>
|
||||
<filename>repmgr.conf</filename> for the stopped node *must* be supplied explicitly if not
|
||||
otherwise available.
|
||||
</para>
|
||||
@@ -71,7 +76,7 @@
|
||||
</para>
|
||||
<para>
|
||||
It is only necessary to provide the <application>pg_rewind</application> path
|
||||
if using PostgreSQL 9.3 or 9.4, and <application>pg_rewind</application>
|
||||
if using PostgreSQL 9.4, and <application>pg_rewind</application>
|
||||
is not installed in the PostgreSQL <filename>bin</filename> directory.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -207,9 +212,18 @@
|
||||
a standby to the current primary, not another standby.
|
||||
</para>
|
||||
<para>
|
||||
The node must have been shut down cleanly; if this was not the case, it will
|
||||
need to be manually started (remove any existing <filename>recovery.conf</filename> file first)
|
||||
until it has reached a consistent recovery point, then shut down cleanly.
|
||||
The node's PostgreSQL instance must have been shut down cleanly. If this was not the
|
||||
case, it will need to be started up until it has reached a consistent recovery point,
|
||||
then shut down cleanly.
|
||||
</para>
|
||||
<para>
|
||||
In PostgreSQL 13 and later, this will be done automatically
|
||||
if the <option>--force-rewind</option> is provided (even if an actual rewind
|
||||
is not necessary).
|
||||
</para>
|
||||
<para>
|
||||
With PostgreSQL 12 and earlier, PostgreSQL will need to
|
||||
be started and shut down manually; see below for the best way to do this.
|
||||
</para>
|
||||
<tip>
|
||||
<para>
|
||||
@@ -221,11 +235,14 @@
|
||||
rm -f /var/lib/pgsql/data/recovery.conf
|
||||
postgres --single -D /var/lib/pgsql/data/ < /dev/null</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Note that <filename>standby.signal</filename> (PostgreSQL 11 and earlier:
|
||||
<filename>recovery.conf</filename>) <emphasis>must</emphasis> be removed
|
||||
from the data directory for PostgreSQL to be able to start in single
|
||||
user mode.
|
||||
</para>
|
||||
</tip>
|
||||
<para>
|
||||
&repmgr; will attempt to verify whether the node can rejoin as-is, or whether
|
||||
<command>pg_rewind</command> must be used (see following section).
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="repmgr-node-rejoin-pg-rewind" xreflabel="Using pg_rewind">
|
||||
@@ -241,7 +258,7 @@
|
||||
<command>repmgr node rejoin</command> can optionally use <command>pg_rewind</command> to re-integrate a
|
||||
node which has diverged from the rest of the cluster, typically a failed primary.
|
||||
<command>pg_rewind</command> is available in PostgreSQL 9.5 and later as part of the core distribution,
|
||||
and can be installed from external sources for PostgreSQL 9.3 and 9.4.
|
||||
and can be installed from external sources for PostgreSQL 9.4.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
@@ -283,7 +300,15 @@
|
||||
to execute <command>pg_rewind</command> to ensure the node can be rejoined successfully.
|
||||
</para>
|
||||
|
||||
<important>
|
||||
<refsect2 id="repmgr-node-rejoin-pg-rewind-config-files" xreflabel="pg_rewind and configuration files">
|
||||
|
||||
<title><command>pg_rewind</command> and configuration file retention</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_rewind</primary>
|
||||
<secondary>configuration file retention</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
Be aware that if <command>pg_rewind</command> is executed and actually performs a
|
||||
rewind operation, any configuration files in the PostgreSQL data directory will be
|
||||
@@ -291,17 +316,27 @@
|
||||
</para>
|
||||
<para>
|
||||
To prevent this happening, provide a comma-separated list of files to retain
|
||||
using the <literal>--config-file</literal> command line option; the specified files
|
||||
using the <option>--config-file</option> command line option; the specified files
|
||||
will be archived in a temporary directory (whose parent directory can be specified with
|
||||
<literal>--config-archive-dir</literal>) and restored once the rewind operation is
|
||||
complete.
|
||||
<option>--config-archive-dir</option>, default: <filename>/tmp</filename>)
|
||||
and restored once the rewind operation is complete.
|
||||
</para>
|
||||
</important>
|
||||
</refsect2>
|
||||
|
||||
<para>
|
||||
Example, first using <literal>--dry-run</literal>, then actually executing the
|
||||
<literal>node rejoin command</literal>.
|
||||
<programlisting>
|
||||
<refsect2 id="repmgr-node-rejoin-pg-rewind-example" xreflabel="example using repmgr node rejoin and pg_rewind">
|
||||
|
||||
<title>Example using <command>repmgr node rejoin</command> and <command>pg_rewind</command></title>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_rewind</primary>
|
||||
<secondary>configuration file retention</secondary>
|
||||
</indexterm>
|
||||
|
||||
|
||||
<para>
|
||||
Example, first using <option>--dry-run</option>, then actually executing the
|
||||
<literal>node rejoin command</literal>.
|
||||
<programlisting>
|
||||
$ repmgr node rejoin -f /etc/repmgr.conf -d 'host=node3 dbname=repmgr user=repmgr' \
|
||||
--config-files=postgresql.local.conf,postgresql.conf --verbose --force-rewind --dry-run
|
||||
INFO: replication connection to the rejoin target node was successful
|
||||
@@ -317,17 +352,17 @@
|
||||
pg_rewind -D '/var/lib/postgresql/data' --source-server='host=node3 dbname=repmgr user=repmgr'
|
||||
INFO: prerequisites for executing NODE REJOIN are met</programlisting>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
If <option>--force-rewind</option> is used with the <option>--dry-run</option> option,
|
||||
this checks the prerequisites for using <application>pg_rewind</application>, but is
|
||||
not an absolute guarantee that actually executing <application>pg_rewind</application>
|
||||
will succeed. See also section <xref linkend="repmgr-node-rejoin-caveats"/> below.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
If <option>--force-rewind</option> is used with the <option>--dry-run</option> option,
|
||||
this checks the prerequisites for using <application>pg_rewind</application>, but is
|
||||
not an absolute guarantee that actually executing <application>pg_rewind</application>
|
||||
will succeed. See also section <xref linkend="repmgr-node-rejoin-caveats"/> below.
|
||||
</para>
|
||||
|
||||
</note>
|
||||
</note>
|
||||
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
$ repmgr node rejoin -f /etc/repmgr.conf -d 'host=node3 dbname=repmgr user=repmgr' \
|
||||
--config-files=postgresql.local.conf,postgresql.conf --verbose --force-rewind
|
||||
NOTICE: pg_rewind execution required for this node to attach to rejoin target node 3
|
||||
@@ -339,8 +374,8 @@
|
||||
NOTICE: starting server using "pg_ctl -l /var/log/postgres/startup.log -w -D '/var/lib/pgsql/data' start"
|
||||
NOTICE: NODE REJOIN successful
|
||||
DETAIL: node 2 is now attached to node 3</programlisting>
|
||||
</para>
|
||||
|
||||
</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="repmgr-node-rejoin-caveats" xreflabel="Caveats">
|
||||
@@ -369,6 +404,11 @@
|
||||
the current standby's PostgreSQL log will contain entries with the text
|
||||
"<literal>record with incorrect prev-link</literal>".
|
||||
</para>
|
||||
<para>
|
||||
In PostgreSQL 9.5 and earlier, it is <emphasis>not</emphasis> possible to use
|
||||
<application>pg_rewind</application> to attach to a target node with a lower
|
||||
timeline than the local node.
|
||||
</para>
|
||||
<para>
|
||||
We strongly recommend running <command>repmgr node rejoin</command> with the
|
||||
<option>--dry-run</option> option first. Additionally it might be a good idea
|
||||
@@ -378,6 +418,51 @@
|
||||
is running in <option>--dry-run</option> mode.
|
||||
</para>
|
||||
|
||||
<warning>
|
||||
<para>
|
||||
In all current PostgreSQL versions (as of September 2020), <application>pg_rewind</application>
|
||||
contains a corner-case bug which affects standbys in a very specific situation.
|
||||
</para>
|
||||
<para>
|
||||
This situation occurs when a standby was shut down <emphasis>before</emphasis> its
|
||||
primary node, and an attempt is made to attach this standby to another primary
|
||||
in the same cluster (following a "split brain" situation where the standby
|
||||
was connected to the wrong primary). In this case, &repmgr; will correctly determine
|
||||
that <application>pg_rewind</application> should be executed, however
|
||||
<application>pg_rewind</application> incorrectly decides that no action is necessary.
|
||||
</para>
|
||||
<para>
|
||||
In this situation, &repmgr; will report something like:
|
||||
<programlisting>
|
||||
NOTICE: pg_rewind execution required for this node to attach to rejoin target node 1
|
||||
DETAIL: rejoin target server's timeline 3 forked off current database system timeline 2 before current recovery point 0/7019C10</programlisting>
|
||||
but when executed, <application>pg_rewind</application> will report:
|
||||
<programlisting>
|
||||
pg_rewind: servers diverged at WAL location 0/7015540 on timeline 2
|
||||
pg_rewind: no rewind required</programlisting>
|
||||
and if an attempt is made to attach the standby to the new primary, PostgreSQL logs on the standby
|
||||
will contain errors like:
|
||||
<programlisting>
|
||||
[2020-09-07 15:01:41 UTC] LOG: 00000: replication terminated by primary server
|
||||
[2020-09-07 15:01:41 UTC] DETAIL: End of WAL reached on timeline 2 at 0/7015540.
|
||||
[2020-09-07 15:01:41 UTC] LOG: 00000: new timeline 3 forked off current database system timeline 2 before current recovery point 0/7019C10</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Currently it is not possible to resolve this situation using <application>pg_rewind</application>.
|
||||
A <ulink url="https://www.postgresql.org/message-id/flat/CABvVfJU-LDWvoz4-Yow3Ay5LZYTuPD7eSjjE4kGyNZpXC6FrVQ@mail.gmail.com">patch</ulink>
|
||||
has been submitted and will hopefully be included in a forthcoming PostgreSQL minor release.
|
||||
</para>
|
||||
<para>
|
||||
As a workaround, start the primary server the standby was previously attached to,
|
||||
and ensure the standby can be attached to it. If <application>pg_rewind</application> was actually executed,
|
||||
it will have copied in the <filename>.history</filename> file from the target primary server; this must
|
||||
be removed. <command>repmgr node rejoin</command> can then be used to attach the standby to the original
|
||||
primary. Ensure any changes pending on the primary have propogated to the standby. Then shut down the primary
|
||||
server <emphasis>first</emphasis>, before shutting down the standby. It should then be possible to
|
||||
use <command>repmgr node rejoin</command> to attach the standby to the new primary.
|
||||
</para>
|
||||
</warning>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@@ -75,8 +75,22 @@
|
||||
<para>
|
||||
Issue a <command>CHECKPOINT</command> before stopping or restarting the node.
|
||||
</para>
|
||||
<para>
|
||||
Note that a superuser connection is required to be able to execute the
|
||||
<command>CHECKPOINT</command> command.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-S</option>/<option>--superuser</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Connect as the named superuser instead of the normal &repmgr; user.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
@@ -60,6 +60,17 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--force</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Forcibly unregister the node if it is registered as an active primary,
|
||||
as long as it has no registered standbys; or if it is registered as
|
||||
a primary but running as a standby.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
@@ -104,14 +104,6 @@
|
||||
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
|
||||
<listitem>
|
||||
<simpara><varname>standby_mode</varname> (always <literal>'on'</literal>)</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara><varname>recovery_target_timeline</varname> (always <literal>'latest'</literal>)</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara><varname>primary_conninfo</varname></simpara>
|
||||
</listitem>
|
||||
@@ -122,6 +114,21 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
For PostgreSQL 11 and earlier, these parameters will also be set:
|
||||
</para>
|
||||
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara><varname>standby_mode</varname> (always <literal>'on'</literal>)</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara><varname>recovery_target_timeline</varname> (always <literal>'latest'</literal>)</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
<para>
|
||||
The following additional parameters can be specified in <filename>repmgr.conf</filename>
|
||||
for inclusion in the replication configuration:
|
||||
@@ -175,7 +182,10 @@
|
||||
<programlisting>
|
||||
pg_basebackup_options='--wal-method=fetch'</programlisting>
|
||||
|
||||
and ensure that <literal>wal_keep_segments</literal> is set to an appropriately high value.
|
||||
and ensure that <literal>wal_keep_segments</literal> (PostgreSQL 13 and later:
|
||||
<literal>wal_keep_size</literal>) is set to an appropriately high value. Note
|
||||
however that this is not a particularly reliable way of ensuring sufficient
|
||||
WAL is retained and is not recommended.
|
||||
See the <ulink url="https://www.postgresql.org/docs/current/app-pgbasebackup.html">
|
||||
pg_basebackup</ulink> documentation for details.
|
||||
</para>
|
||||
@@ -188,18 +198,37 @@
|
||||
</note>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="repmgr-standby-clone-wal-directory">
|
||||
|
||||
<title>Placing WAL files into a different directory</title>
|
||||
|
||||
<para>
|
||||
To ensure that WAL files are placed in a directory outside of the main data
|
||||
directory (e.g. to keep them on a separate disk for performance reasons),
|
||||
specify the location with <option>--waldir</option>
|
||||
(PostgreSQL 9.6 and earlier: <option>--xlogdir</option>) in
|
||||
the <filename>repmgr.conf</filename> parameter <option>pg_basebackup_options</option>,
|
||||
e.g.:
|
||||
<programlisting>
|
||||
pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
|
||||
This setting will also be honored by &repmgr; when cloning from Barman
|
||||
(&repmgr; 5.2 and later).
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<!-- don't rename this id as it may be used in external links -->
|
||||
<refsect1 id="repmgr-standby-create-recovery-conf">
|
||||
|
||||
<title>Using a standby cloned by another method</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>recovery.conf</primary>
|
||||
<primary>replication configuration</primary>
|
||||
<secondary>generating for a standby cloned by another method</secondary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>replication configuration</primary>
|
||||
<primary>recovery.conf</primary>
|
||||
<secondary>generating for a standby cloned by another method</secondary>
|
||||
</indexterm>
|
||||
|
||||
@@ -224,7 +253,7 @@
|
||||
</para>
|
||||
</tip>
|
||||
<para>
|
||||
Then execute the command <command>repmgr standby clone --recovery-conf-only</command>.
|
||||
Then execute the command <command>repmgr standby clone --replication-conf-only</command>.
|
||||
This will create the <filename>recovery.conf</filename> file needed to attach
|
||||
the node to its upstream (in PostgreSQL 12 and later: append replication configuration
|
||||
to <filename>postgresql.auto.conf</filename>), and will also create a replication slot on the
|
||||
@@ -236,7 +265,7 @@
|
||||
<option>-F/--force</option> option is provided.
|
||||
</para>
|
||||
<para>
|
||||
Execute <command>repmgr standby clone --recovery-conf-only --dry-run</command>
|
||||
Execute <command>repmgr standby clone --replication-conf-only --dry-run</command>
|
||||
to check the prerequisites for creating the recovery configuration,
|
||||
and display the contents of the configuration which would be added without actually
|
||||
making any changes.
|
||||
@@ -266,7 +295,7 @@
|
||||
Check prerequisites but don't actually clone the standby.
|
||||
</para>
|
||||
<para>
|
||||
If <option>--recovery-conf-only</option> specified, the contents of
|
||||
If <option>--replication-conf-only</option> specified, the contents of
|
||||
the generated recovery configuration will be displayed
|
||||
but not written.
|
||||
</para>
|
||||
@@ -312,7 +341,7 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option> --recovery-conf-only</option></term>
|
||||
<term><option>--replication-conf-only</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Create recovery configuration for a previously cloned instance.
|
||||
@@ -370,6 +399,23 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--verify-backup</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
<!-- update link after Pg13 release -->
|
||||
Verify a cloned node using the
|
||||
<ulink url="https://www.postgresql.org/docs/13/app-pgverifybackup.html">pg_verifybackup</ulink>
|
||||
utility (PostgreSQL 13 and later).
|
||||
</para>
|
||||
<para>
|
||||
This option can currently only be used when cloning directly from an upstream
|
||||
node.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--without-barman </option></term>
|
||||
<listitem>
|
||||
|
||||
@@ -47,7 +47,15 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This command will force a restart of PostgreSQL on the standby node.
|
||||
In PostgreSQL 12 and earlier, this command will force a restart of PostgreSQL on the standby node.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In PostgreSQL 13 and later, by default this command will signal PostgreSQL to reload its
|
||||
configuration, which will cause PostgreSQL to follow the new upstream without
|
||||
a restart. If this behaviour is not desired for whatever reason, the configuration
|
||||
file parameter <varname>standby_follow_restart</varname> can be set <literal>true</literal>
|
||||
to always force a restart.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
||||
@@ -66,10 +66,10 @@
|
||||
Both values can be defined in <filename>repmgr.conf</filename>.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<warning>
|
||||
<para>
|
||||
If WAL replay is paused on the standby, and not all WAL files on the standby have been
|
||||
replayed, &repmgr; will not attempt to promote it.
|
||||
In PostgreSQL 12 and earlier, if WAL replay is paused on the standby, and not all
|
||||
WAL files on the standby have been replayed, &repmgr; will not attempt to promote it.
|
||||
</para>
|
||||
<para>
|
||||
This is because if WAL replay is paused, PostgreSQL itself will not react to a promote command
|
||||
@@ -81,7 +81,10 @@
|
||||
Note that if the standby is in archive recovery, &repmgr; will not be able to determine
|
||||
if more WAL is pending replay, and will abort the promotion attempt if WAL replay is paused.
|
||||
</para>
|
||||
</note>
|
||||
<para>
|
||||
This restriction does <emphasis>not</emphasis> apply to PostgreSQL 13 and later.
|
||||
</para>
|
||||
</warning>
|
||||
|
||||
</refsect1>
|
||||
|
||||
@@ -95,7 +98,6 @@
|
||||
NOTICE: promoting standby to primary
|
||||
DETAIL: promoting server "node2" (ID: 2) using "pg_ctl -l /var/log/postgres/startup.log -w -D '/var/lib/postgres/data' promote"
|
||||
server promoting
|
||||
DEBUG: setting node 2 as primary and marking existing primary as failed
|
||||
NOTICE: STANDBY PROMOTE successful
|
||||
DETAIL: server "node2" (ID: 2) was successfully promoted to primary</programlisting>
|
||||
</para>
|
||||
@@ -104,26 +106,35 @@
|
||||
|
||||
<refsect1>
|
||||
<title>User permission requirements</title>
|
||||
<para><emphasis>pg_promote() (PostgreSQL 12)</emphasis></para>
|
||||
<para><emphasis>pg_promote() (PostgreSQL 12 and later)</emphasis></para>
|
||||
<para>
|
||||
From PostgreSQL 12, &repmgr; uses the <command>pg_promote()</command> function to promote a standby
|
||||
to primary.
|
||||
From PostgreSQL 12, &repmgr; will attempt to use the built-in <function>pg_promote()</function>
|
||||
function to promote a standby to primary.
|
||||
</para>
|
||||
<para>
|
||||
By default, execution of <command>pg_promote()</command> is restricted to superusers.
|
||||
If the <literal>repmgr</literal> use is not a superuser, execution permission for this
|
||||
function must be granted with e.g.:
|
||||
<programlisting>
|
||||
By default, execution of <function>pg_promote()</function> is restricted to superusers.
|
||||
If the <literal>repmgr</literal> user does not have permission to execute
|
||||
<function>pg_promote()</function>, &repmgr; will fall back to using "<command>pg_ctl promote</command>".
|
||||
</para>
|
||||
<tip>
|
||||
<para>
|
||||
Execute <command>repmgr standby promote</command> with the <option>--dry-run</option>
|
||||
to check whether the &repmgr; user has permission to execute <function>pg_promote()</function>.
|
||||
</para>
|
||||
<para>
|
||||
If the <literal>repmgr</literal> user is not a superuser, execution permission for this
|
||||
function can be granted with e.g.:
|
||||
<programlisting>
|
||||
GRANT EXECUTE ON FUNCTION pg_catalog.pg_promote TO repmgr</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Note that permissions are only effective for the database they are granted in, so
|
||||
this <emphasis>must</emphasis> be executed in the &repmgr; database to be effective.
|
||||
</para>
|
||||
|
||||
</tip>
|
||||
<para>
|
||||
A future &repmgr; release will relax this restriction by falling back to
|
||||
<command>pg_ctl promote</command>, as used for pre-PostgreSQL 12 versions.
|
||||
For more details on <function>pg_promote()</function>, see the
|
||||
<ulink url="https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-RECOVERY-CONTROL-TABLE">PostgreSQL documentation</ulink>.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@@ -161,6 +172,42 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-F</option></term>
|
||||
<term><option>--force</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Ignore warnings and continue anyway.
|
||||
</para>
|
||||
<para>
|
||||
This option is relevant in the following situations if <option>--siblings-follow</option> was specified:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara>
|
||||
If one or more sibling nodes was not reachable via SSH, the standby will be promoted anyway.
|
||||
</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara>
|
||||
If the promotion candidate has insufficient free walsenders to accomodate the standbys which will
|
||||
be attached to it, the standby will be promoted anyway.
|
||||
</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara>
|
||||
If replication slots are in use but the promotion candidate has insufficient free replication slots
|
||||
to accomodate the standbys which will be attached to it, the standby will be promoted anyway.
|
||||
</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Note that if the <option>-F</option>/<option>--force</option> option is used when any of the above
|
||||
situations is encountered, the onus is on the user to manually resolve any resulting issues.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
@@ -198,6 +245,23 @@
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<indexterm>
|
||||
<primary>service_promote_command</primary>
|
||||
<secondary>with "repmgr standby promote "</secondary>
|
||||
</indexterm>
|
||||
<simpara>
|
||||
<literal>service_promote_command</literal>:
|
||||
a command which will be executed instead of <command>pg_ctl promote</command>
|
||||
or (in PostgreSQL 12 and later) <function>pg_promote()</function>.
|
||||
</simpara>
|
||||
<simpara>
|
||||
This is intended for systems which provide a package-level promote command,
|
||||
such as Debian's <application>pg_ctlcluster</application>, to promote the
|
||||
PostgreSQL from standby to primary.
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<command><ulink url="https://docs.pgbarman.org/#recover">barman recover</ulink></command>
|
||||
command), register the node as detailed in section
|
||||
<xref linkend="repmgr-standby-register-inactive-node"/> then execute
|
||||
<link linkend="repmgr-standby-create-recovery-conf">repmgr standby clone --recovery-conf-only</link>
|
||||
<link linkend="repmgr-standby-create-recovery-conf">repmgr standby clone --replication-conf-only</link>
|
||||
to generate the appropriate replication configuration.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@@ -67,28 +67,29 @@
|
||||
<para><emphasis>CHECKPOINT</emphasis></para>
|
||||
<para>
|
||||
&repmgr; executes <command>CHECKPOINT</command> on the demotion candidate as part of the shutdown
|
||||
process.
|
||||
process to ensure it shuts down as smoothly as possible.
|
||||
</para>
|
||||
<para>
|
||||
Note that <command>CHECKPOINT</command> requires database superuser permissions to execute.
|
||||
If the <literal>repmgr</literal> user is not a superuser, the checkpoint operation will
|
||||
fail, though this is not a fatal error &repmgr; will continue the switchover process.
|
||||
</para>
|
||||
<para><emphasis>pg_promote() (PostgreSQL 12)</emphasis></para>
|
||||
<para>
|
||||
From PostgreSQL 12, &repmgr; uses the <command>pg_promote()</command> function to promote a standby
|
||||
to primary.
|
||||
If the <literal>repmgr</literal> user is not a superuser, the name of a superuser should be
|
||||
provided with the <option>-S</option>/<option>--superuser</option>.
|
||||
</para>
|
||||
<para>
|
||||
By default, execution of <command>pg_promote()</command> is restricted to superusers.
|
||||
If the <literal>repmgr</literal> use is not a superuser, execution permission for this
|
||||
function must be granted with e.g.:
|
||||
<programlisting>
|
||||
GRANT EXECUTE ON FUNCTION pg_catalog.pg_promote TO repmgr</programlisting>
|
||||
If &repmgr; is unable to execute the <command>CHECKPOINT</command> command, the switchover
|
||||
can still be carried out, albeit at a greater risk that the demotion candidate may not
|
||||
be able to shut down as smoothly as might otherwise have been the case.
|
||||
</para>
|
||||
<para><emphasis>pg_promote() (PostgreSQL 12 and later)</emphasis></para>
|
||||
<para>
|
||||
From PostgreSQL 12, &repmgr; defaults to using the built-in <command>pg_promote()</command> function to
|
||||
promote a standby to primary.
|
||||
</para>
|
||||
<para>
|
||||
A future &repmgr; release will relax this restriction by falling back to
|
||||
<command>pg_ctl promote</command>, as used for pre-PostgreSQL 12 versions.
|
||||
Note that execution of <function>pg_promote()</function> is restricted to superusers or to
|
||||
any user who has been granted execution permission for this function. If the &repmgr; user
|
||||
is not permitted to execute <function>pg_promote()</function>, &repmgr; will fall back to using
|
||||
"<command>pg_ctl promote</command>". For more details see
|
||||
<link linkend="repmgr-standby-promote">repmgr standby promote</link>.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@@ -146,7 +147,7 @@
|
||||
<para>
|
||||
Use <application>pg_rewind</application> to reintegrate the old primary if necessary
|
||||
(and the prerequisites for using <application>pg_rewind</application> are met).
|
||||
If using PostgreSQL 9.3 or 9.4, and the <application>pg_rewind</application>
|
||||
If using PostgreSQL 9.4, and the <application>pg_rewind</application>
|
||||
binary is not installed in the PostgreSQL <filename>bin</filename> directory,
|
||||
provide its full path. For more details see also <xref linkend="switchover-pg-rewind"/>.
|
||||
</para>
|
||||
@@ -215,6 +216,17 @@
|
||||
</note>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-S</option>/<option>--superuser</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Use the named superuser instead of the normal &repmgr; user to perform
|
||||
actions requiring superuser permissions.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
@@ -63,6 +63,34 @@
|
||||
</refsect1>
|
||||
|
||||
|
||||
|
||||
<refsect1>
|
||||
|
||||
<title>Options</title>
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--dry-run</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Check prerequisites but don't actually register the witness
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-F</option>/<option>--force</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Overwrite an existing node record
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="repmgr-witness-register-events">
|
||||
<title>Event notifications</title>
|
||||
<para>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<abstract>
|
||||
<para>
|
||||
This is the official documentation of &repmgr; &repmgrversion; for
|
||||
use with PostgreSQL 9.3 - PostgreSQL 12.
|
||||
use with PostgreSQL 9.4 - PostgreSQL 13.
|
||||
</para>
|
||||
<para>
|
||||
&repmgr; is being continually developed and we strongly recommend using the
|
||||
@@ -91,7 +91,6 @@
|
||||
&repmgrd-automatic-failover;
|
||||
&repmgrd-configuration;
|
||||
&repmgrd-operation;
|
||||
&repmgrd-bdr;
|
||||
</part>
|
||||
|
||||
<part id="repmgr-command-reference">
|
||||
|
||||
@@ -331,11 +331,12 @@
|
||||
To use this, <option>failover_validation_command</option> in <filename>repmgr.conf</filename>
|
||||
to a script executable by the <literal>postgres</literal> system user, e.g.:
|
||||
<programlisting>
|
||||
failover_validation_command=/path/to/script.sh %n %a</programlisting>
|
||||
failover_validation_command=/path/to/script.sh %n</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <literal>%n</literal> parameter will be replaced with the node ID, and the
|
||||
<literal>%a</literal> parameter will be replaced by the node name when the script is executed.
|
||||
The <literal>%n</literal> parameter will be replaced with the node ID when the script is
|
||||
executed. A number of other parameters are also available, see section
|
||||
"<xref linkend="repmgrd-automatic-failover-configuration-optional"/>" for details.
|
||||
</para>
|
||||
<para>
|
||||
This script must return an exit code of <literal>0</literal> to indicate the node should promote itself.
|
||||
|
||||
@@ -1,429 +0,0 @@
|
||||
<chapter id="repmgrd-bdr">
|
||||
<title>BDR failover with repmgrd</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>repmgrd</primary>
|
||||
<secondary>BDR</secondary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>BDR</primary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
&repmgr; 4.x provides support for monitoring a pair of BDR 2.x nodes and taking action in
|
||||
case one of the nodes fails.
|
||||
</para>
|
||||
<note>
|
||||
<simpara>
|
||||
Due to the nature of BDR 1.x/2.x, it's only safe to use this solution for
|
||||
a two-node scenario. Introducing additional nodes will create an inherent
|
||||
risk of node desynchronisation if a node goes down without being cleanly
|
||||
removed from the cluster.
|
||||
</simpara>
|
||||
</note>
|
||||
<para>
|
||||
In contrast to streaming replication, there's no concept of "promoting" a new
|
||||
primary node with BDR. Instead, "failover" involves monitoring both nodes
|
||||
with &repmgrd; and redirecting queries from the failed node to the remaining
|
||||
active node. This can be done by using an
|
||||
<link linkend="event-notifications">event notification</link> script
|
||||
which is called by &repmgrd; to dynamically
|
||||
reconfigure a proxy server/connection pooler such as <application>PgBouncer</application>.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<simpara>
|
||||
This &repmgr; functionality is for BDR 2.x only running on PostgreSQL 9.4/9.6.
|
||||
It is <emphasis>not</emphasis> required for later BDR versions.
|
||||
</simpara>
|
||||
</note>
|
||||
|
||||
<sect1 id="bdr-prerequisites" xreflabel="BDR prequisites">
|
||||
<title>Prerequisites</title>
|
||||
<important>
|
||||
<para>
|
||||
This &repmgr; functionality is for BDR 2.x only running on PostgreSQL 9.4/9.6.
|
||||
It is <emphasis>not</emphasis> required for later BDR versions.
|
||||
</para>
|
||||
</important>
|
||||
<para>
|
||||
&repmgr; 4 requires PostgreSQL 9.4 or 9.6 with the BDR 2 extension
|
||||
enabled and configured for a two-node BDR network. &repmgr; 4 packages
|
||||
must be installed on each node before attempting to configure
|
||||
<application>repmgr</application>.
|
||||
</para>
|
||||
<note>
|
||||
<simpara>
|
||||
&repmgr; 4 will refuse to install if it detects more than two BDR nodes.
|
||||
</simpara>
|
||||
</note>
|
||||
<para>
|
||||
Application database connections *must* be passed through a proxy server/
|
||||
connection pooler such as <application>PgBouncer</application>, and it must be possible to dynamically
|
||||
reconfigure that from &repmgrd;. The example demonstrated in this document
|
||||
will use <application>PgBouncer</application>
|
||||
</para>
|
||||
<para>
|
||||
The proxy server / connection poolers must <emphasis>not</emphasis>
|
||||
be installed on the database servers.
|
||||
</para>
|
||||
<para>
|
||||
For this example, it's assumed password-less SSH connections are available
|
||||
from the PostgreSQL servers to the servers where <application>PgBouncer</application>
|
||||
runs, and that the user on those servers has permission to alter the
|
||||
<application>PgBouncer</application> configuration files.
|
||||
</para>
|
||||
<para>
|
||||
PostgreSQL connections must be possible between each node, and each node
|
||||
must be able to connect to each PgBouncer instance.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="bdr-configuration" xreflabel="BDR configuration">
|
||||
<title>Configuration</title>
|
||||
<para>
|
||||
A sample configuration for <filename>repmgr.conf</filename> on each
|
||||
BDR node would look like this:
|
||||
<programlisting>
|
||||
# Node information
|
||||
node_id=1
|
||||
node_name='node1'
|
||||
conninfo='host=node1 dbname=bdrtest user=repmgr connect_timeout=2'
|
||||
data_directory='/var/lib/postgresql/data'
|
||||
replication_type='bdr'
|
||||
|
||||
# Event notification configuration
|
||||
event_notifications='bdr_failover'
|
||||
event_notification_command='/path/to/bdr-pgbouncer.sh %n %e %s "%c" "%a" >> /tmp/bdr-failover.log 2>&1'
|
||||
|
||||
# repmgrd options
|
||||
monitor_interval_secs=5
|
||||
reconnect_attempts=6
|
||||
reconnect_interval=5</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Adjust settings as appropriate; copy and adjust for the second node (particularly
|
||||
the values <varname>node_id</varname>, <varname>node_name</varname>
|
||||
and <varname>conninfo</varname>).
|
||||
</para>
|
||||
<para>
|
||||
Note that the values provided for the <varname>conninfo</varname> string
|
||||
must be valid for connections from <emphasis>both</emphasis> nodes in the
|
||||
replication cluster. The database must be the BDR-enabled database.
|
||||
</para>
|
||||
<para>
|
||||
If defined, the <varname>event_notifications</varname> parameter will restrict
|
||||
execution of the script defined in <varname>event_notification_command</varname>
|
||||
to the specified event(s).
|
||||
</para>
|
||||
<note>
|
||||
<simpara>
|
||||
<varname>event_notification_command</varname> is the script which does the actual "heavy lifting"
|
||||
of reconfiguring the proxy server/ connection pooler. It is fully
|
||||
user-definable; see section <xref linkend="bdr-event-notification-command"/> for a reference
|
||||
implementation.
|
||||
</simpara>
|
||||
</note>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="bdr-repmgr-setup" xreflabel="repmgr setup with BDR">
|
||||
<title>repmgr setup</title>
|
||||
<para>
|
||||
Register both nodes; example on <literal>node1</literal>:
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf bdr register
|
||||
NOTICE: attempting to install extension "repmgr"
|
||||
NOTICE: "repmgr" extension successfully installed
|
||||
NOTICE: node record created for node 'node1' (ID: 1)
|
||||
NOTICE: BDR node 1 registered (conninfo: host=node1 dbname=bdrtest user=repmgr)</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
and on <literal>node1</literal>:
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf bdr register
|
||||
NOTICE: node record created for node 'node2' (ID: 2)
|
||||
NOTICE: BDR node 2 registered (conninfo: host=node2 dbname=bdrtest user=repmgr)</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
The <literal>repmgr</literal> extension will be automatically created
|
||||
when the first node is registered, and will be propagated to the second
|
||||
node.
|
||||
</para>
|
||||
<important>
|
||||
<simpara>
|
||||
Ensure the &repmgr; package is available on both nodes before
|
||||
attempting to register the first node.
|
||||
</simpara>
|
||||
</important>
|
||||
<para>
|
||||
At this point the meta data for both nodes has been created; executing
|
||||
<xref linkend="repmgr-cluster-show"/> (on either node) should produce output like this:
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf cluster show
|
||||
ID | Name | Role | Status | Upstream | Location | Connection string
|
||||
----+-------+------+-----------+----------+--------------------------------------------------------
|
||||
1 | node1 | bdr | * running | | default | host=node1 dbname=bdrtest user=repmgr connect_timeout=2
|
||||
2 | node2 | bdr | * running | | default | host=node2 dbname=bdrtest user=repmgr connect_timeout=2</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Additionally it's possible to display log of significant events; executing
|
||||
<xref linkend="repmgr-cluster-event"/> (on either node) should produce output like this:
|
||||
<programlisting>
|
||||
$ repmgr -f /etc/repmgr.conf cluster event
|
||||
Node ID | Event | OK | Timestamp | Details
|
||||
---------+--------------+----+---------------------+----------------------------------------------
|
||||
2 | bdr_register | t | 2017-07-27 17:51:48 | node record created for node 'node2' (ID: 2)
|
||||
1 | bdr_register | t | 2017-07-27 17:51:00 | node record created for node 'node1' (ID: 1)
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
At this point there will only be records for the two node registrations (displayed here
|
||||
in reverse chronological order).
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="bdr-event-notification-command" xreflabel="Defining the BDR failover "event_notification command"">
|
||||
<title>Defining the BDR failover "event_notification_command"</title>
|
||||
<para>
|
||||
Key to "failover" execution is the <literal>event_notification_command</literal>,
|
||||
which is a user-definable script specified in <filename>repmpgr.conf</filename>
|
||||
and which can use a &repmgr; <link linkend="event-notifications">event notification</link>
|
||||
to reconfigure the proxy server / connection pooler so it points to the other, still-active node.
|
||||
Details of the event will be passed as parameters to the script.
|
||||
</para>
|
||||
<para>
|
||||
Following parameter placeholders are available for the script definition in <filename>repmpgr.conf</filename>;
|
||||
these will be replaced with the appropriate value when the script is executed:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>%n</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
node ID
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>%e</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
event type
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>%t</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
success (1 or 0)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>%t</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
timestamp
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>%d</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
details
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>%c</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
conninfo string of the next available node (<varname>bdr_failover</varname> and <varname>bdr_recovery</varname>)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>%a</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
name of the next available node (<varname>bdr_failover</varname> and <varname>bdr_recovery</varname>)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>
|
||||
Note that <literal>%c</literal> and <literal>%a</literal> are only provided with
|
||||
particular failover events, in this case <varname>bdr_failover</varname>.
|
||||
</para>
|
||||
<para>
|
||||
The provided sample script
|
||||
(<literal><ulink url="https://raw.githubusercontent.com/2ndQuadrant/repmgr/master/scripts/bdr-pgbouncer.sh">scripts/bdr-pgbouncer.sh</ulink></literal>)
|
||||
is configured as follows:
|
||||
<programlisting>
|
||||
event_notification_command='/path/to/bdr-pgbouncer.sh %n %e %s "%c" "%a"'</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
and parses the placeholder parameters like this:
|
||||
<programlisting>
|
||||
NODE_ID=$1
|
||||
EVENT_TYPE=$2
|
||||
SUCCESS=$3
|
||||
NEXT_CONNINFO=$4
|
||||
NEXT_NODE_NAME=$5</programlisting>
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
The sample script also contains some hard-coded values for the <application>PgBouncer</application>
|
||||
configuration for both nodes; these will need to be adjusted for your local environment
|
||||
(ideally the scripts would be maintained as templates and generated by some
|
||||
kind of provisioning system).
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
The script performs following steps:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara>pauses <application>PgBouncer</application> on all nodes</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara>recreates the <application>PgBouncer</application> configuration file on each
|
||||
node using the information provided by &repmgrd;
|
||||
(primarily the <varname>conninfo</varname> string) to configure
|
||||
<application>PgBouncer</application></simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara>reloads the <application>PgBouncer</application> configuration</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara>executes the <command>RESUME</command> command (in <application>PgBouncer</application>)</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Following successful script execution, any connections to PgBouncer on the failed BDR node
|
||||
will be redirected to the active node.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="bdr-monitoring-failover" xreflabel="Node monitoring and failover">
|
||||
<title>Node monitoring and failover</title>
|
||||
<para>
|
||||
At the intervals specified by <varname>monitor_interval_secs</varname>
|
||||
in <filename>repmgr.conf</filename>, &repmgrd;
|
||||
will ping each node to check if it's available. If a node isn't available,
|
||||
&repmgrd; will enter failover mode and check <varname>reconnect_attempts</varname>
|
||||
times at intervals of <varname>reconnect_interval</varname> to confirm the node is definitely unreachable.
|
||||
This buffer period is necessary to avoid false positives caused by transient
|
||||
network outages.
|
||||
</para>
|
||||
<para>
|
||||
If the node is still unavailable, &repmgrd; will enter failover mode and execute
|
||||
the script defined in <varname>event_notification_command</varname>; an entry will be logged
|
||||
in the <literal>repmgr.events</literal> table and &repmgrd; will
|
||||
(unless otherwise configured) resume monitoring of the node in "degraded" mode until it reappears.
|
||||
</para>
|
||||
<para>
|
||||
&repmgrd; logfile output during a failover event will look something like this
|
||||
on one node (usually the node which has failed, here <literal>node2</literal>):
|
||||
<programlisting>
|
||||
...
|
||||
[2017-07-27 21:08:39] [INFO] starting continuous BDR node monitoring
|
||||
[2017-07-27 21:08:39] [INFO] monitoring BDR replication status on node "node2" (ID: 2)
|
||||
[2017-07-27 21:08:55] [INFO] monitoring BDR replication status on node "node2" (ID: 2)
|
||||
[2017-07-27 21:09:11] [INFO] monitoring BDR replication status on node "node2" (ID: 2)
|
||||
[2017-07-27 21:09:23] [WARNING] unable to connect to node node2 (ID 2)
|
||||
[2017-07-27 21:09:23] [INFO] checking state of node 2, 0 of 5 attempts
|
||||
[2017-07-27 21:09:23] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:24] [INFO] checking state of node 2, 1 of 5 attempts
|
||||
[2017-07-27 21:09:24] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:25] [INFO] checking state of node 2, 2 of 5 attempts
|
||||
[2017-07-27 21:09:25] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:26] [INFO] checking state of node 2, 3 of 5 attempts
|
||||
[2017-07-27 21:09:26] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:27] [INFO] checking state of node 2, 4 of 5 attempts
|
||||
[2017-07-27 21:09:27] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:28] [WARNING] unable to reconnect to node 2 after 5 attempts
|
||||
[2017-07-27 21:09:28] [NOTICE] setting node record for node 2 to inactive
|
||||
[2017-07-27 21:09:28] [INFO] executing notification command for event "bdr_failover"
|
||||
[2017-07-27 21:09:28] [DETAIL] command is:
|
||||
/path/to/bdr-pgbouncer.sh 2 bdr_failover 1 "host=host=node1 dbname=bdrtest user=repmgr connect_timeout=2" "node1"
|
||||
[2017-07-27 21:09:28] [INFO] node 'node2' (ID: 2) detected as failed; next available node is 'node1' (ID: 1)
|
||||
[2017-07-27 21:09:28] [INFO] monitoring BDR replication status on node "node2" (ID: 2)
|
||||
[2017-07-27 21:09:28] [DETAIL] monitoring node "node2" (ID: 2) in degraded mode
|
||||
...</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Output on the other node (<literal>node1</literal>) during the same event will look like this:
|
||||
<programlisting>
|
||||
...
|
||||
[2017-07-27 21:08:35] [INFO] starting continuous BDR node monitoring
|
||||
[2017-07-27 21:08:35] [INFO] monitoring BDR replication status on node "node1" (ID: 1)
|
||||
[2017-07-27 21:08:51] [INFO] monitoring BDR replication status on node "node1" (ID: 1)
|
||||
[2017-07-27 21:09:07] [INFO] monitoring BDR replication status on node "node1" (ID: 1)
|
||||
[2017-07-27 21:09:23] [WARNING] unable to connect to node node2 (ID 2)
|
||||
[2017-07-27 21:09:23] [INFO] checking state of node 2, 0 of 5 attempts
|
||||
[2017-07-27 21:09:23] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:24] [INFO] checking state of node 2, 1 of 5 attempts
|
||||
[2017-07-27 21:09:24] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:25] [INFO] checking state of node 2, 2 of 5 attempts
|
||||
[2017-07-27 21:09:25] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:26] [INFO] checking state of node 2, 3 of 5 attempts
|
||||
[2017-07-27 21:09:26] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:27] [INFO] checking state of node 2, 4 of 5 attempts
|
||||
[2017-07-27 21:09:27] [INFO] sleeping 1 seconds until next reconnection attempt
|
||||
[2017-07-27 21:09:28] [WARNING] unable to reconnect to node 2 after 5 attempts
|
||||
[2017-07-27 21:09:28] [NOTICE] other node's repmgrd is handling failover
|
||||
[2017-07-27 21:09:28] [INFO] monitoring BDR replication status on node "node1" (ID: 1)
|
||||
[2017-07-27 21:09:28] [DETAIL] monitoring node "node2" (ID: 2) in degraded mode
|
||||
...</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
This assumes only the PostgreSQL instance on <literal>node2</literal> has failed. In this case the
|
||||
&repmgrd; instance running on <literal>node2</literal> has performed the failover. However if
|
||||
the entire server becomes unavailable, &repmgrd; on <literal>node1</literal> will perform
|
||||
the failover.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="bdr-node-recovery" xreflabel="Node recovery">
|
||||
<title>Node recovery</title>
|
||||
<para>
|
||||
Following failure of a BDR node, if the node subsequently becomes available again,
|
||||
a <varname>bdr_recovery</varname> event will be generated. This could potentially be used to
|
||||
reconfigure PgBouncer automatically to bring the node back into the available pool,
|
||||
however it would be prudent to manually verify the node's status before
|
||||
exposing it to the application.
|
||||
</para>
|
||||
<para>
|
||||
If the failed node comes back up and connects correctly, output similar to this
|
||||
will be visible in the &repmgrd; log:
|
||||
<programlisting>
|
||||
[2017-07-27 21:25:30] [DETAIL] monitoring node "node2" (ID: 2) in degraded mode
|
||||
[2017-07-27 21:25:46] [INFO] monitoring BDR replication status on node "node2" (ID: 2)
|
||||
[2017-07-27 21:25:46] [DETAIL] monitoring node "node2" (ID: 2) in degraded mode
|
||||
[2017-07-27 21:25:55] [INFO] active replication slot for node "node1" found after 1 seconds
|
||||
[2017-07-27 21:25:55] [NOTICE] node "node2" (ID: 2) has recovered after 986 seconds</programlisting>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="bdr-complete-shutdown" xreflabel="Shutdown of both nodes">
|
||||
<title>Shutdown of both nodes</title>
|
||||
<para>
|
||||
If both PostgreSQL instances are shut down, &repmgrd; will try and handle the
|
||||
situation as gracefully as possible, though with no failover candidates available
|
||||
there's not much it can do. Should this case ever occur, we recommend shutting
|
||||
down &repmgrd; on both nodes and restarting it once the PostgreSQL instances
|
||||
are running properly.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="repmgrd-automatic-failover-configuration-optional">
|
||||
<sect2 id="repmgrd-automatic-failover-configuration-optional" xreflabel="Optional configuration for automatic failover">
|
||||
<title>Optional configuration for automatic failover</title>
|
||||
|
||||
<para>
|
||||
@@ -370,8 +370,8 @@
|
||||
</para>
|
||||
</note>
|
||||
<para>
|
||||
One or both of the following parameter placeholders
|
||||
should be provided, which will be replaced by repmgrd with the appropriate
|
||||
One or more of the following parameter placeholders
|
||||
may be provided, which will be replaced by repmgrd with the appropriate
|
||||
value:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
@@ -380,6 +380,15 @@
|
||||
<listitem>
|
||||
<simpara><literal>%a</literal>: node name</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%v</literal>: number of visible nodes</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%u</literal>: number of shared upstream nodes</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><literal>%t</literal>: total number of nodes</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
@@ -410,6 +419,33 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>always_promote</option></term>
|
||||
|
||||
<listitem>
|
||||
<indexterm>
|
||||
<primary>always_promote</primary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
Default: <literal>false</literal>.
|
||||
</para>
|
||||
<para>
|
||||
If <literal>true</literal>, promote the local node even if its
|
||||
&repmgr; metadata is not up-to-date.
|
||||
</para>
|
||||
<para>
|
||||
Normally &repmgr; expects its metadata (stored in the <varname>repmgr.nodes</varname>
|
||||
table) to be up-to-date so &repmgrd; can take the correct action during a failover.
|
||||
However it's possible that updates made on the primary may not
|
||||
have propagated to the standby (promotion candidate). In this case &repmgrd; will
|
||||
default to not promoting the standby. This behaviour can be overridden by setting
|
||||
<option>always_promote</option> to <literal>true</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry>
|
||||
|
||||
<term><option>standby_disconnect_on_failover</option></term>
|
||||
@@ -624,18 +660,6 @@ repmgrd_service_stop_command='sudo systemctl repmgr12 stop'
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<varname>bdr_local_monitoring_only</varname>
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<varname>bdr_recovery_timeout</varname>
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<varname>child_nodes_check_interval</varname>
|
||||
@@ -768,6 +792,12 @@ repmgrd_service_stop_command='sudo systemctl repmgr12 stop'
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<varname>always_promote</varname>
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
<varname>promote_command</varname>
|
||||
|
||||
@@ -108,7 +108,7 @@ The actual script is as follows; adjust the configurable items as appropriate:
|
||||
|
||||
# 1. Promote this node from standby to primary
|
||||
|
||||
repmgr standby promote -f /etc/repmgr.conf
|
||||
repmgr standby promote -f /etc/repmgr.conf --log-to-file
|
||||
|
||||
# 2. Reconfigure pgbouncer instances
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ NOTICE: node 3 (node3) paused</programlisting>
|
||||
If the primary becomes available again (e.g. following a software upgrade), &repmgrd;
|
||||
will automatically reconnect, e.g.:
|
||||
<programlisting>
|
||||
[2019-08-28 12:25:41] [NOTICE] reconnected to upstream node 1 after 8 seconds, resuming monitoring</programlisting>
|
||||
[2019-08-28 12:25:41] [NOTICE] reconnected to upstream node "node1" (ID: 1) after 8 seconds, resuming monitoring</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -295,7 +295,7 @@ NOTICE: node 3 (node3) unpaused</programlisting>
|
||||
[2017-08-29 10:59:37] [HINT] use "repmgr standby promote" to manually promote this node
|
||||
[2017-08-29 10:59:37] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in degraded state (automatic failover disabled)
|
||||
[2017-08-29 10:59:53] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in degraded state (automatic failover disabled)
|
||||
[2017-08-29 11:00:45] [NOTICE] reconnected to upstream node 1 after 68 seconds, resuming monitoring
|
||||
[2017-08-29 11:00:45] [NOTICE] reconnected to upstream node "node1" (ID: 1) after 68 seconds, resuming monitoring
|
||||
[2017-08-29 11:00:57] [INFO] node "node2" (ID: 2) monitoring upstream node "node1" (ID: 1) in normal state (automatic failover disabled)</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
@@ -244,16 +244,15 @@
|
||||
</para>
|
||||
<para>
|
||||
<application>pg_rewind</application> has been part of the core PostgreSQL distribution since
|
||||
version 9.5. Users of versions 9.3 and 9.4 will need to manually install it; the source code is available here:
|
||||
version 9.5. Users of PostgreSQL 9.4 will need to manually install it; the source code is available here:
|
||||
<ulink url="https://github.com/vmware/pg_rewind">https://github.com/vmware/pg_rewind</ulink>.
|
||||
If the <application>pg_rewind</application>
|
||||
binary is not installed in the PostgreSQL <filename>bin</filename> directory, provide
|
||||
its full path on the demotion candidate with <option>--force-rewind</option>.
|
||||
</para>
|
||||
<para>
|
||||
Note that building the 9.3/9.4 version of <application>pg_rewind</application> requires the PostgreSQL
|
||||
source code. Also, PostgreSQL 9.3 does not provide <varname>wal_log_hints</varname>,
|
||||
meaning data checksums must have been enabled when the database was initialized.
|
||||
Note that building the 9.4 version of <application>pg_rewind</application> requires the PostgreSQL
|
||||
source code.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@@ -343,7 +342,7 @@
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem>
|
||||
<simpara>
|
||||
If using PostgreSQL 9.3 or 9.4, you should ensure that the shutdown command
|
||||
If using PostgreSQL 9.4, you should ensure that the shutdown command
|
||||
is configured to use PostgreSQL's <varname>fast</varname> shutdown mode (the default in 9.5
|
||||
and later). If relying on <command>pg_ctl</command> to perform database server operations,
|
||||
you should include <literal>-m fast</literal> in <varname>pg_ctl_options</varname>
|
||||
|
||||
@@ -201,9 +201,13 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
</para>
|
||||
<tip>
|
||||
<para>
|
||||
If the &repmgr; upgrade requires a PostgreSQL restart, combine the &repmgr; upgrade
|
||||
with a PostgreSQL minor version upgrade, which will require a restart in any case.
|
||||
New PostgreSQL minor version are usually released every couple of months.
|
||||
If the &repmgr; upgrade requires a PostgreSQL restart, combine the &repmgr; upgrade
|
||||
with a PostgreSQL minor version upgrade, which will require a restart in any case.
|
||||
</para>
|
||||
<para>
|
||||
New PostgreSQL minor versions are usually released every couple of months;
|
||||
see the <ulink url="https://www.postgresql.org/developer/roadmap/">Roadmap</ulink>
|
||||
for the current schedule.
|
||||
</para>
|
||||
</tip>
|
||||
</sect2>
|
||||
@@ -269,6 +273,29 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
</para>
|
||||
</tip>
|
||||
|
||||
<sect2 id="upgrading-pg-upgrade-standby" xreflabel="pg_upgrade and upgrading standbys">
|
||||
<title>Upgrading standbys with pg_upgrade and rsync</title>
|
||||
<para>
|
||||
If you are intending to upgrade a standby using the <command>rsync</command> method described
|
||||
in the <ulink url="https://www.postgresql.org/docs/current/pgupgrade.html#PGUPGRADE-STEP-REPLICAS">pg_upgrade documentation</ulink>,
|
||||
you <emphasis>must</emphasis> ensure the standby's replication configuration is present and correct
|
||||
before starting the standby.
|
||||
</para>
|
||||
<para>
|
||||
Use <link linkend="repmgr-standby-clone">repmgr standby clone --replication-conf-only</link> to generate
|
||||
the correct replication configuration.
|
||||
</para>
|
||||
|
||||
<tip>
|
||||
<para>
|
||||
If upgrading from PostgreSQL 11 or earlier, be sure to delete <filename>recovery.conf</filename>, if present,
|
||||
otherwise PostgreSQL will refuse to start.
|
||||
</para>
|
||||
</tip>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
@@ -285,12 +312,12 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<simpara>
|
||||
converting the repmgr.conf configuration files
|
||||
converting the <filename>repmgr.conf</filename> configuration files
|
||||
</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara>
|
||||
upgrading the repmgr schema using <command>CREATE EXTENSION</command>
|
||||
upgrading the repmgr schema using <command>CREATE EXTENSION</command> (PostgreSQL 12 and earlier)
|
||||
</simpara>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
@@ -434,22 +461,31 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
<para>
|
||||
Please note that the the conversion script will add an empty
|
||||
placeholder parameter for <varname>data_directory</varname>, which
|
||||
is a required parameter from &repmgr; 4.
|
||||
is a required parameter from &repmgr; 4. This must be manually modified to contain
|
||||
the correct data directory.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Upgrading the repmgr schema</title>
|
||||
<title>Upgrading the repmgr schema (PostgreSQL 12 and earlier)</title>
|
||||
<para>
|
||||
Ensure &repmgrd; is not running, or any cron jobs which execute the
|
||||
<command>repmgr</command> binary.
|
||||
</para>
|
||||
<para>
|
||||
Install <literal>repmgr 4</literal> packages; any <literal>repmgr 3.x</literal> packages
|
||||
Install the latest &repmgr; package; any <literal>repmgr 3.x</literal> packages
|
||||
should be uninstalled (if not automatically uninstalled already by your packaging system).
|
||||
</para>
|
||||
<sect3>
|
||||
<title>Upgrading from repmgr 3.1.1 or earlier</title>
|
||||
<tip>
|
||||
<simpara>
|
||||
If you don't care about any data from the existing &repmgr; installation,
|
||||
(e.g. the contents of the <structname>events</structname> and <structname>monitoring</structname>
|
||||
tables), the follwing steps can be skipped; proceed to <xref linkend="upgrade-reregister-nodes"/>.
|
||||
</simpara>
|
||||
</tip>
|
||||
|
||||
<para>
|
||||
If your repmgr version is 3.1.1 or earlier, you will need to update
|
||||
the schema to the latest version in the 3.x series (3.3.2) before
|
||||
@@ -478,19 +514,37 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
<para>
|
||||
In the database used by the existing &repmgr; installation, execute:
|
||||
<programlisting>
|
||||
CREATE EXTENSION repmgr FROM unpackaged;</programlisting>
|
||||
CREATE EXTENSION repmgr FROM unpackaged</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This will move and convert all objects from the existing schema
|
||||
into the new, standard <literal>repmgr</literal> schema.
|
||||
</para>
|
||||
<note>
|
||||
<simpara>there must be only one schema matching <literal>repmgr_%</literal> in the
|
||||
<simpara>There must be only one schema matching <literal>repmgr_%</literal> in the
|
||||
database, otherwise this step may not work.
|
||||
</simpara>
|
||||
</note>
|
||||
</sect3>
|
||||
<sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Upgrading the repmgr schema (PostgreSQL 13 and later)</title>
|
||||
<para>
|
||||
Beginning with PostgreSQL 13, the <command>CREATE EXTENSION ... FROM unpackaged</command>
|
||||
syntax is no longer available. In the unlikely event you have ended up with an
|
||||
installation running PostgreSQL 13 or later and containing the legacy &repmgr;
|
||||
schema, there is no convenient way of upgrading this; instead you'll just need
|
||||
to re-register the nodes as detailed in <link linkend="upgrade-reregister-nodes">the following section</link>,
|
||||
which will create the &repmgr; extension automatically.
|
||||
</para>
|
||||
<para>
|
||||
Any historical data you wish to retain (e.g. the contents of the <structname>events</structname>
|
||||
and <structname>monitoring</structname> tables) will need to be exported manually.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="upgrade-reregister-nodes">
|
||||
<title>Re-register each node</title>
|
||||
<para>
|
||||
This is necessary to update the <literal>repmgr</literal> metadata with some additional items.
|
||||
@@ -500,6 +554,10 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
<programlisting>
|
||||
repmgr primary register -f /etc/repmgr.conf --force</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
If not already present (e.g. after executing <command>CREATE EXTENSION repmgr FROM unpackaged</command>),
|
||||
the &repmgr; extension will be automatically created by <command>repmgr primary register</command>.
|
||||
</para>
|
||||
<para>
|
||||
On each standby node, execute e.g.
|
||||
<programlisting>
|
||||
@@ -512,18 +570,20 @@ ALTER EXTENSION repmgr UPDATE</programlisting>
|
||||
<para>
|
||||
The original <literal>repmgr_$cluster</literal> schema can be dropped at any time.
|
||||
</para>
|
||||
<tip>
|
||||
<simpara>
|
||||
If you don't care about any data from the existing &repmgr; installation,
|
||||
(e.g. the contents of the <structname>events</structname> and <structname>monitoring</structname>
|
||||
tables), the manual <command>CREATE EXTENSION</command> step can be skipped; just re-register
|
||||
each node, starting with the primary node, and the <literal>repmgr</literal> extension will be
|
||||
automatically created.
|
||||
</simpara>
|
||||
</tip>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="upgrade-drop-repmgr-cluster-schema">
|
||||
<title>Drop the legacy repmgr schema</title>
|
||||
<para>
|
||||
Once the cluster has been registered with the current &repmgr; version, the legacy
|
||||
<literal>repmgr_$cluster</literal> schema can be dropped at any time with:
|
||||
<programlisting>
|
||||
DROP SCHEMA repmgr_$cluster CASCADE</programlisting>
|
||||
(substitute <literal>$cluster</literal> with the value of the <varname>clustername</varname>
|
||||
variable used in &repmgr; 3.x).
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* errcode.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -32,18 +32,6 @@ SELECT * FROM repmgr.show_nodes;
|
||||
(0 rows)
|
||||
|
||||
-- functions
|
||||
SELECT repmgr.am_bdr_failover_handler(-1);
|
||||
am_bdr_failover_handler
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT repmgr.am_bdr_failover_handler(NULL);
|
||||
am_bdr_failover_handler
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT repmgr.get_new_primary();
|
||||
get_new_primary
|
||||
-----------------
|
||||
@@ -92,9 +80,3 @@ SELECT repmgr.standby_set_last_updated();
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT repmgr.unset_bdr_failover_handler();
|
||||
unset_bdr_failover_handler
|
||||
----------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
|
||||
2
log.c
2
log.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* log.c - Logging methods
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
2
log.h
2
log.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* log.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
5
repmgr--5.0--5.1.sql
Normal file
5
repmgr--5.0--5.1.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
|
||||
DROP FUNCTION am_bdr_failover_handler(INT);
|
||||
DROP FUNCTION unset_bdr_failover_handler();
|
||||
@@ -153,16 +153,6 @@ CREATE FUNCTION reset_voting_status()
|
||||
AS 'MODULE_PATHNAME', 'reset_voting_status'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION am_bdr_failover_handler(INT)
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'am_bdr_failover_handler'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION unset_bdr_failover_handler()
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'unset_bdr_failover_handler'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
|
||||
|
||||
7
repmgr--5.1--5.2.sql
Normal file
7
repmgr--5.1--5.2.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
|
||||
SELECT pg_catalog.pg_extension_config_dump('repmgr.nodes', '');
|
||||
SELECT pg_catalog.pg_extension_config_dump('repmgr.events', '');
|
||||
SELECT pg_catalog.pg_extension_config_dump('repmgr.monitoring_history', '');
|
||||
|
||||
214
repmgr--5.1.sql
Normal file
214
repmgr--5.1.sql
Normal file
@@ -0,0 +1,214 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
|
||||
CREATE TABLE repmgr.nodes (
|
||||
node_id INTEGER PRIMARY KEY,
|
||||
upstream_node_id INTEGER NULL REFERENCES nodes (node_id) DEFERRABLE,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
node_name TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN('primary','standby','witness','bdr')),
|
||||
location TEXT NOT NULL DEFAULT 'default',
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
conninfo TEXT NOT NULL,
|
||||
repluser VARCHAR(63) NOT NULL,
|
||||
slot_name TEXT NULL,
|
||||
config_file TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE repmgr.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
|
||||
);
|
||||
|
||||
DO $repmgr$
|
||||
DECLARE
|
||||
DECLARE server_version_num INT;
|
||||
BEGIN
|
||||
SELECT setting
|
||||
FROM pg_catalog.pg_settings
|
||||
WHERE name = 'server_version_num'
|
||||
INTO server_version_num;
|
||||
IF server_version_num >= 90400 THEN
|
||||
EXECUTE $repmgr_func$
|
||||
CREATE TABLE repmgr.monitoring_history (
|
||||
primary_node_id INTEGER NOT NULL,
|
||||
standby_node_id INTEGER NOT NULL,
|
||||
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
last_apply_time TIMESTAMP WITH TIME ZONE,
|
||||
last_wal_primary_location PG_LSN NOT NULL,
|
||||
last_wal_standby_location PG_LSN,
|
||||
replication_lag BIGINT NOT NULL,
|
||||
apply_lag BIGINT NOT NULL
|
||||
)
|
||||
$repmgr_func$;
|
||||
ELSE
|
||||
EXECUTE $repmgr_func$
|
||||
CREATE TABLE repmgr.monitoring_history (
|
||||
primary_node_id INTEGER NOT NULL,
|
||||
standby_node_id INTEGER NOT NULL,
|
||||
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
last_apply_time TIMESTAMP WITH TIME ZONE,
|
||||
last_wal_primary_location TEXT NOT NULL,
|
||||
last_wal_standby_location TEXT,
|
||||
replication_lag BIGINT NOT NULL,
|
||||
apply_lag BIGINT NOT NULL
|
||||
)
|
||||
$repmgr_func$;
|
||||
END IF;
|
||||
END$repmgr$;
|
||||
|
||||
|
||||
|
||||
CREATE INDEX idx_monitoring_history_time
|
||||
ON repmgr.monitoring_history (last_monitor_time, standby_node_id);
|
||||
|
||||
CREATE VIEW repmgr.show_nodes AS
|
||||
SELECT n.node_id,
|
||||
n.node_name,
|
||||
n.active,
|
||||
n.upstream_node_id,
|
||||
un.node_name AS upstream_node_name,
|
||||
n.type,
|
||||
n.priority,
|
||||
n.conninfo
|
||||
FROM repmgr.nodes n
|
||||
LEFT JOIN repmgr.nodes un
|
||||
ON un.node_id = n.upstream_node_id;
|
||||
|
||||
CREATE TABLE repmgr.voting_term (
|
||||
term INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX voting_term_restrict
|
||||
ON repmgr.voting_term ((TRUE));
|
||||
|
||||
CREATE RULE voting_term_delete AS
|
||||
ON DELETE TO repmgr.voting_term
|
||||
DO INSTEAD NOTHING;
|
||||
|
||||
|
||||
/* ================= */
|
||||
/* repmgrd functions */
|
||||
/* ================= */
|
||||
|
||||
/* monitoring functions */
|
||||
|
||||
CREATE FUNCTION set_local_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_local_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_set_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_set_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_get_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_get_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_last_seen(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_last_seen()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
/* failover functions */
|
||||
|
||||
CREATE FUNCTION notify_follow_primary(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'notify_follow_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_new_primary()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_new_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION reset_voting_status()
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'reset_voting_status'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pidfile()
|
||||
RETURNS TEXT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pidfile'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_repmgrd_pid(INT, TEXT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_repmgrd_pid'
|
||||
LANGUAGE C CALLED ON NULL INPUT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_running()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_running'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_pause(BOOL)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_pause'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_paused()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_paused'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_wal_receiver_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_wal_receiver_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
|
||||
|
||||
|
||||
/* views */
|
||||
|
||||
CREATE VIEW repmgr.replication_status AS
|
||||
SELECT m.primary_node_id, m.standby_node_id, n.node_name AS standby_name,
|
||||
n.type AS node_type, n.active, last_monitor_time,
|
||||
CASE WHEN n.type='standby' THEN m.last_wal_primary_location ELSE NULL END AS last_wal_primary_location,
|
||||
m.last_wal_standby_location,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.replication_lag) ELSE NULL END AS replication_lag,
|
||||
CASE WHEN n.type='standby' THEN
|
||||
CASE WHEN replication_lag > 0 THEN age(now(), m.last_apply_time) ELSE '0'::INTERVAL END
|
||||
ELSE NULL
|
||||
END AS replication_time_lag,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.apply_lag) ELSE NULL END AS apply_lag,
|
||||
AGE(NOW(), CASE WHEN pg_catalog.pg_is_in_recovery() THEN repmgr.standby_get_last_updated() ELSE m.last_monitor_time END) AS communication_time_lag
|
||||
FROM repmgr.monitoring_history m
|
||||
JOIN repmgr.nodes n ON m.standby_node_id = n.node_id
|
||||
WHERE (m.standby_node_id, m.last_monitor_time) IN (
|
||||
SELECT m1.standby_node_id, MAX(m1.last_monitor_time)
|
||||
FROM repmgr.monitoring_history m1 GROUP BY 1
|
||||
);
|
||||
|
||||
192
repmgr--5.2.sql
Normal file
192
repmgr--5.2.sql
Normal file
@@ -0,0 +1,192 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
|
||||
CREATE TABLE repmgr.nodes (
|
||||
node_id INTEGER PRIMARY KEY,
|
||||
upstream_node_id INTEGER NULL REFERENCES nodes (node_id) DEFERRABLE,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
node_name TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN('primary','standby','witness','bdr')),
|
||||
location TEXT NOT NULL DEFAULT 'default',
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
conninfo TEXT NOT NULL,
|
||||
repluser VARCHAR(63) NOT NULL,
|
||||
slot_name TEXT NULL,
|
||||
config_file TEXT NOT NULL
|
||||
);
|
||||
|
||||
SELECT pg_catalog.pg_extension_config_dump('repmgr.nodes', '');
|
||||
|
||||
CREATE TABLE repmgr.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
|
||||
);
|
||||
|
||||
SELECT pg_catalog.pg_extension_config_dump('repmgr.events', '');
|
||||
|
||||
CREATE TABLE repmgr.monitoring_history (
|
||||
primary_node_id INTEGER NOT NULL,
|
||||
standby_node_id INTEGER NOT NULL,
|
||||
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
last_apply_time TIMESTAMP WITH TIME ZONE,
|
||||
last_wal_primary_location PG_LSN NOT NULL,
|
||||
last_wal_standby_location PG_LSN,
|
||||
replication_lag BIGINT NOT NULL,
|
||||
apply_lag BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_monitoring_history_time
|
||||
ON repmgr.monitoring_history (last_monitor_time, standby_node_id);
|
||||
|
||||
SELECT pg_catalog.pg_extension_config_dump('repmgr.monitoring_history', '');
|
||||
|
||||
CREATE VIEW repmgr.show_nodes AS
|
||||
SELECT n.node_id,
|
||||
n.node_name,
|
||||
n.active,
|
||||
n.upstream_node_id,
|
||||
un.node_name AS upstream_node_name,
|
||||
n.type,
|
||||
n.priority,
|
||||
n.conninfo
|
||||
FROM repmgr.nodes n
|
||||
LEFT JOIN repmgr.nodes un
|
||||
ON un.node_id = n.upstream_node_id;
|
||||
|
||||
CREATE TABLE repmgr.voting_term (
|
||||
term INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX voting_term_restrict
|
||||
ON repmgr.voting_term ((TRUE));
|
||||
|
||||
CREATE RULE voting_term_delete AS
|
||||
ON DELETE TO repmgr.voting_term
|
||||
DO INSTEAD NOTHING;
|
||||
|
||||
|
||||
/* ================= */
|
||||
/* repmgrd functions */
|
||||
/* ================= */
|
||||
|
||||
/* monitoring functions */
|
||||
|
||||
CREATE FUNCTION set_local_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_local_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_set_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_set_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_get_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_get_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_last_seen(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_last_seen()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
/* failover functions */
|
||||
|
||||
CREATE FUNCTION notify_follow_primary(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'notify_follow_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_new_primary()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_new_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION reset_voting_status()
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'reset_voting_status'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pidfile()
|
||||
RETURNS TEXT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pidfile'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_repmgrd_pid(INT, TEXT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_repmgrd_pid'
|
||||
LANGUAGE C CALLED ON NULL INPUT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_running()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_running'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_pause(BOOL)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_pause'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_paused()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_paused'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_wal_receiver_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_wal_receiver_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
|
||||
|
||||
|
||||
/* views */
|
||||
|
||||
CREATE VIEW repmgr.replication_status AS
|
||||
SELECT m.primary_node_id, m.standby_node_id, n.node_name AS standby_name,
|
||||
n.type AS node_type, n.active, last_monitor_time,
|
||||
CASE WHEN n.type='standby' THEN m.last_wal_primary_location ELSE NULL END AS last_wal_primary_location,
|
||||
m.last_wal_standby_location,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.replication_lag) ELSE NULL END AS replication_lag,
|
||||
CASE WHEN n.type='standby' THEN
|
||||
CASE WHEN replication_lag > 0 THEN age(now(), m.last_apply_time) ELSE '0'::INTERVAL END
|
||||
ELSE NULL
|
||||
END AS replication_time_lag,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.apply_lag) ELSE NULL END AS apply_lag,
|
||||
AGE(NOW(), CASE WHEN pg_catalog.pg_is_in_recovery() THEN repmgr.standby_get_last_updated() ELSE m.last_monitor_time END) AS communication_time_lag
|
||||
FROM repmgr.monitoring_history m
|
||||
JOIN repmgr.nodes n ON m.standby_node_id = n.node_id
|
||||
WHERE (m.standby_node_id, m.last_monitor_time) IN (
|
||||
SELECT m1.standby_node_id, MAX(m1.last_monitor_time)
|
||||
FROM repmgr.monitoring_history m1 GROUP BY 1
|
||||
);
|
||||
|
||||
265
repmgr--unpackaged--5.1.sql
Normal file
265
repmgr--unpackaged--5.1.sql
Normal file
@@ -0,0 +1,265 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
|
||||
-- extract the current schema name
|
||||
-- NOTE: this assumes there will be only one schema matching 'repmgr_%';
|
||||
-- user is responsible for ensuring this is the case
|
||||
|
||||
CREATE TEMPORARY TABLE repmgr_old_schema (schema_name TEXT);
|
||||
INSERT INTO repmgr_old_schema (schema_name)
|
||||
SELECT nspname AS schema_name
|
||||
FROM pg_catalog.pg_namespace
|
||||
WHERE nspname LIKE 'repmgr_%'
|
||||
LIMIT 1;
|
||||
|
||||
-- move old objects into new schema
|
||||
DO $repmgr$
|
||||
DECLARE
|
||||
old_schema TEXT;
|
||||
BEGIN
|
||||
SELECT schema_name FROM repmgr_old_schema
|
||||
INTO old_schema;
|
||||
EXECUTE format('ALTER TABLE %I.repl_nodes SET SCHEMA repmgr', old_schema);
|
||||
EXECUTE format('ALTER TABLE %I.repl_events SET SCHEMA repmgr', old_schema);
|
||||
EXECUTE format('ALTER TABLE %I.repl_monitor SET SCHEMA repmgr', old_schema);
|
||||
EXECUTE format('DROP VIEW IF EXISTS %I.repl_show_nodes', old_schema);
|
||||
EXECUTE format('DROP VIEW IF EXISTS %I.repl_status', old_schema);
|
||||
END$repmgr$;
|
||||
|
||||
-- convert "repmgr_$cluster.repl_nodes" to "repmgr.nodes"
|
||||
CREATE TABLE repmgr.nodes (
|
||||
node_id INTEGER PRIMARY KEY,
|
||||
upstream_node_id INTEGER NULL REFERENCES repmgr.nodes (node_id) DEFERRABLE,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
node_name TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN('primary','standby','witness','bdr')),
|
||||
location TEXT NOT NULL DEFAULT 'default',
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
conninfo TEXT NOT NULL,
|
||||
repluser VARCHAR(63) NOT NULL,
|
||||
slot_name TEXT NULL,
|
||||
config_file TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO repmgr.nodes
|
||||
(node_id, upstream_node_id, active, node_name, type, location, priority, conninfo, repluser, slot_name, config_file)
|
||||
SELECT id, upstream_node_id, active, name,
|
||||
CASE WHEN type = 'master' THEN 'primary' ELSE type END,
|
||||
'default', priority, conninfo, 'unknown', slot_name, 'unknown'
|
||||
FROM repmgr.repl_nodes
|
||||
ORDER BY id;
|
||||
|
||||
|
||||
-- convert "repmgr_$cluster.repl_event" to "event"
|
||||
|
||||
ALTER TABLE repmgr.repl_events RENAME TO events;
|
||||
|
||||
-- create new table "repmgr.voting_term"
|
||||
CREATE TABLE repmgr.voting_term (
|
||||
term INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX voting_term_restrict
|
||||
ON repmgr.voting_term ((TRUE));
|
||||
|
||||
CREATE RULE voting_term_delete AS
|
||||
ON DELETE TO repmgr.voting_term
|
||||
DO INSTEAD NOTHING;
|
||||
|
||||
INSERT INTO repmgr.voting_term (term) VALUES (1);
|
||||
|
||||
|
||||
-- convert "repmgr_$cluster.repl_monitor" to "monitoring_history"
|
||||
|
||||
|
||||
DO $repmgr$
|
||||
DECLARE
|
||||
DECLARE server_version_num INT;
|
||||
BEGIN
|
||||
SELECT setting
|
||||
FROM pg_catalog.pg_settings
|
||||
WHERE name = 'server_version_num'
|
||||
INTO server_version_num;
|
||||
IF server_version_num >= 90400 THEN
|
||||
EXECUTE $repmgr_func$
|
||||
CREATE TABLE repmgr.monitoring_history (
|
||||
primary_node_id INTEGER NOT NULL,
|
||||
standby_node_id INTEGER NOT NULL,
|
||||
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
last_apply_time TIMESTAMP WITH TIME ZONE,
|
||||
last_wal_primary_location PG_LSN NOT NULL,
|
||||
last_wal_standby_location PG_LSN,
|
||||
replication_lag BIGINT NOT NULL,
|
||||
apply_lag BIGINT NOT NULL
|
||||
)
|
||||
$repmgr_func$;
|
||||
INSERT INTO repmgr.monitoring_history
|
||||
(primary_node_id, standby_node_id, last_monitor_time, last_apply_time, last_wal_primary_location, last_wal_standby_location, replication_lag, apply_lag)
|
||||
SELECT primary_node, standby_node, last_monitor_time, last_apply_time, last_wal_primary_location::pg_lsn, last_wal_standby_location::pg_lsn, replication_lag, apply_lag
|
||||
FROM repmgr.repl_monitor;
|
||||
ELSE
|
||||
EXECUTE $repmgr_func$
|
||||
CREATE TABLE repmgr.monitoring_history (
|
||||
primary_node_id INTEGER NOT NULL,
|
||||
standby_node_id INTEGER NOT NULL,
|
||||
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
last_apply_time TIMESTAMP WITH TIME ZONE,
|
||||
last_wal_primary_location TEXT NOT NULL,
|
||||
last_wal_standby_location TEXT,
|
||||
replication_lag BIGINT NOT NULL,
|
||||
apply_lag BIGINT NOT NULL
|
||||
)
|
||||
$repmgr_func$;
|
||||
INSERT INTO repmgr.monitoring_history
|
||||
(primary_node_id, standby_node_id, last_monitor_time, last_apply_time, last_wal_primary_location, last_wal_standby_location, replication_lag, apply_lag)
|
||||
SELECT primary_node, standby_node, last_monitor_time, last_apply_time, last_wal_primary_location, last_wal_standby_location, replication_lag, apply_lag
|
||||
FROM repmgr.repl_monitor;
|
||||
|
||||
END IF;
|
||||
END$repmgr$;
|
||||
|
||||
CREATE INDEX idx_monitoring_history_time
|
||||
ON repmgr.monitoring_history (last_monitor_time, standby_node_id);
|
||||
|
||||
CREATE VIEW repmgr.show_nodes AS
|
||||
SELECT n.node_id,
|
||||
n.node_name,
|
||||
n.active,
|
||||
n.upstream_node_id,
|
||||
un.node_name AS upstream_node_name,
|
||||
n.type,
|
||||
n.priority,
|
||||
n.conninfo
|
||||
FROM repmgr.nodes n
|
||||
LEFT JOIN repmgr.nodes un
|
||||
ON un.node_id = n.upstream_node_id;
|
||||
|
||||
|
||||
/* ================= */
|
||||
/* repmgrd functions */
|
||||
/* ================= */
|
||||
|
||||
/* monitoring functions */
|
||||
|
||||
CREATE FUNCTION set_local_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_local_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_set_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_set_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_get_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_get_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_last_seen(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_last_seen()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
/* failover functions */
|
||||
|
||||
CREATE FUNCTION notify_follow_primary(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'notify_follow_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_new_primary()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_new_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION reset_voting_status()
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'reset_voting_status'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pidfile()
|
||||
RETURNS TEXT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pidfile'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_repmgrd_pid(INT, TEXT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_repmgrd_pid'
|
||||
LANGUAGE C CALLED ON NULL INPUT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_running()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_running'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_pause(BOOL)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_pause'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_paused()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_paused'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_wal_receiver_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_wal_receiver_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
|
||||
/* views */
|
||||
|
||||
CREATE VIEW repmgr.replication_status AS
|
||||
SELECT m.primary_node_id, m.standby_node_id, n.node_name AS standby_name,
|
||||
n.type AS node_type, n.active, last_monitor_time,
|
||||
CASE WHEN n.type='standby' THEN m.last_wal_primary_location ELSE NULL END AS last_wal_primary_location,
|
||||
m.last_wal_standby_location,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.replication_lag) ELSE NULL END AS replication_lag,
|
||||
CASE WHEN n.type='standby' THEN
|
||||
CASE WHEN replication_lag > 0 THEN age(now(), m.last_apply_time) ELSE '0'::INTERVAL END
|
||||
ELSE NULL
|
||||
END AS replication_time_lag,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.apply_lag) ELSE NULL END AS apply_lag,
|
||||
AGE(NOW(), CASE WHEN pg_catalog.pg_is_in_recovery() THEN repmgr.standby_get_last_updated() ELSE m.last_monitor_time END) AS communication_time_lag
|
||||
FROM repmgr.monitoring_history m
|
||||
JOIN repmgr.nodes n ON m.standby_node_id = n.node_id
|
||||
WHERE (m.standby_node_id, m.last_monitor_time) IN (
|
||||
SELECT m1.standby_node_id, MAX(m1.last_monitor_time)
|
||||
FROM repmgr.monitoring_history m1 GROUP BY 1
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* drop old tables */
|
||||
DROP TABLE repmgr.repl_nodes;
|
||||
DROP TABLE repmgr.repl_monitor;
|
||||
|
||||
-- remove temporary table
|
||||
DROP TABLE repmgr_old_schema;
|
||||
245
repmgr--unpackaged--5.2.sql
Normal file
245
repmgr--unpackaged--5.2.sql
Normal file
@@ -0,0 +1,245 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
|
||||
-- extract the current schema name
|
||||
-- NOTE: this assumes there will be only one schema matching 'repmgr_%';
|
||||
-- user is responsible for ensuring this is the case
|
||||
|
||||
CREATE TEMPORARY TABLE repmgr_old_schema (schema_name TEXT);
|
||||
INSERT INTO repmgr_old_schema (schema_name)
|
||||
SELECT nspname AS schema_name
|
||||
FROM pg_catalog.pg_namespace
|
||||
WHERE nspname LIKE 'repmgr_%'
|
||||
LIMIT 1;
|
||||
|
||||
-- move old objects into new schema
|
||||
DO $repmgr$
|
||||
DECLARE
|
||||
old_schema TEXT;
|
||||
BEGIN
|
||||
SELECT schema_name FROM repmgr_old_schema
|
||||
INTO old_schema;
|
||||
EXECUTE format('ALTER TABLE %I.repl_nodes SET SCHEMA repmgr', old_schema);
|
||||
EXECUTE format('ALTER TABLE %I.repl_events SET SCHEMA repmgr', old_schema);
|
||||
EXECUTE format('ALTER TABLE %I.repl_monitor SET SCHEMA repmgr', old_schema);
|
||||
EXECUTE format('DROP VIEW IF EXISTS %I.repl_show_nodes', old_schema);
|
||||
EXECUTE format('DROP VIEW IF EXISTS %I.repl_status', old_schema);
|
||||
END$repmgr$;
|
||||
|
||||
-- convert "repmgr_$cluster.repl_nodes" to "repmgr.nodes"
|
||||
CREATE TABLE repmgr.nodes (
|
||||
node_id INTEGER PRIMARY KEY,
|
||||
upstream_node_id INTEGER NULL REFERENCES repmgr.nodes (node_id) DEFERRABLE,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
node_name TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN('primary','standby','witness','bdr')),
|
||||
location TEXT NOT NULL DEFAULT 'default',
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
conninfo TEXT NOT NULL,
|
||||
repluser VARCHAR(63) NOT NULL,
|
||||
slot_name TEXT NULL,
|
||||
config_file TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO repmgr.nodes
|
||||
(node_id, upstream_node_id, active, node_name, type, location, priority, conninfo, repluser, slot_name, config_file)
|
||||
SELECT id, upstream_node_id, active, name,
|
||||
CASE WHEN type = 'master' THEN 'primary' ELSE type END,
|
||||
'default', priority, conninfo, 'unknown', slot_name, 'unknown'
|
||||
FROM repmgr.repl_nodes
|
||||
ORDER BY id;
|
||||
|
||||
|
||||
-- convert "repmgr_$cluster.repl_event" to "event"
|
||||
|
||||
CREATE TABLE repmgr.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
|
||||
);
|
||||
|
||||
INSERT INTO repmgr.events
|
||||
(node_id, event, successful, event_timestamp, details)
|
||||
SELECT node_id, event, successful, event_timestamp, details
|
||||
FROM repmgr.repl_events;
|
||||
|
||||
-- create new table "repmgr.voting_term"
|
||||
CREATE TABLE repmgr.voting_term (
|
||||
term INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX voting_term_restrict
|
||||
ON repmgr.voting_term ((TRUE));
|
||||
|
||||
CREATE RULE voting_term_delete AS
|
||||
ON DELETE TO repmgr.voting_term
|
||||
DO INSTEAD NOTHING;
|
||||
|
||||
INSERT INTO repmgr.voting_term (term) VALUES (1);
|
||||
|
||||
-- convert "repmgr_$cluster.repl_monitor" to "monitoring_history"
|
||||
|
||||
CREATE TABLE repmgr.monitoring_history (
|
||||
primary_node_id INTEGER NOT NULL,
|
||||
standby_node_id INTEGER NOT NULL,
|
||||
last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
last_apply_time TIMESTAMP WITH TIME ZONE,
|
||||
last_wal_primary_location PG_LSN NOT NULL,
|
||||
last_wal_standby_location PG_LSN,
|
||||
replication_lag BIGINT NOT NULL,
|
||||
apply_lag BIGINT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO repmgr.monitoring_history
|
||||
(primary_node_id, standby_node_id, last_monitor_time, last_apply_time, last_wal_primary_location, last_wal_standby_location, replication_lag, apply_lag)
|
||||
SELECT primary_node, standby_node, last_monitor_time, last_apply_time, last_wal_primary_location::pg_lsn, last_wal_standby_location::pg_lsn, replication_lag, apply_lag
|
||||
FROM repmgr.repl_monitor;
|
||||
|
||||
CREATE INDEX idx_monitoring_history_time
|
||||
ON repmgr.monitoring_history (last_monitor_time, standby_node_id);
|
||||
|
||||
CREATE VIEW repmgr.show_nodes AS
|
||||
SELECT n.node_id,
|
||||
n.node_name,
|
||||
n.active,
|
||||
n.upstream_node_id,
|
||||
un.node_name AS upstream_node_name,
|
||||
n.type,
|
||||
n.priority,
|
||||
n.conninfo
|
||||
FROM repmgr.nodes n
|
||||
LEFT JOIN repmgr.nodes un
|
||||
ON un.node_id = n.upstream_node_id;
|
||||
|
||||
|
||||
/* ================= */
|
||||
/* repmgrd functions */
|
||||
/* ================= */
|
||||
|
||||
/* monitoring functions */
|
||||
|
||||
CREATE FUNCTION set_local_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_local_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_set_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_set_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_get_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'standby_get_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_last_seen(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_last_seen()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
/* failover functions */
|
||||
|
||||
CREATE FUNCTION notify_follow_primary(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'notify_follow_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_new_primary()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_new_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION reset_voting_status()
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'reset_voting_status'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_repmgrd_pidfile()
|
||||
RETURNS TEXT
|
||||
AS 'MODULE_PATHNAME', 'get_repmgrd_pidfile'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_repmgrd_pid(INT, TEXT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'set_repmgrd_pid'
|
||||
LANGUAGE C CALLED ON NULL INPUT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_running()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_running'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_pause(BOOL)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_pause'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION repmgrd_is_paused()
|
||||
RETURNS BOOL
|
||||
AS 'MODULE_PATHNAME', 'repmgrd_is_paused'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_wal_receiver_pid()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'get_wal_receiver_pid'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
|
||||
/* views */
|
||||
|
||||
CREATE VIEW repmgr.replication_status AS
|
||||
SELECT m.primary_node_id, m.standby_node_id, n.node_name AS standby_name,
|
||||
n.type AS node_type, n.active, last_monitor_time,
|
||||
CASE WHEN n.type='standby' THEN m.last_wal_primary_location ELSE NULL END AS last_wal_primary_location,
|
||||
m.last_wal_standby_location,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.replication_lag) ELSE NULL END AS replication_lag,
|
||||
CASE WHEN n.type='standby' THEN
|
||||
CASE WHEN replication_lag > 0 THEN age(now(), m.last_apply_time) ELSE '0'::INTERVAL END
|
||||
ELSE NULL
|
||||
END AS replication_time_lag,
|
||||
CASE WHEN n.type='standby' THEN pg_catalog.pg_size_pretty(m.apply_lag) ELSE NULL END AS apply_lag,
|
||||
AGE(NOW(), CASE WHEN pg_catalog.pg_is_in_recovery() THEN repmgr.standby_get_last_updated() ELSE m.last_monitor_time END) AS communication_time_lag
|
||||
FROM repmgr.monitoring_history m
|
||||
JOIN repmgr.nodes n ON m.standby_node_id = n.node_id
|
||||
WHERE (m.standby_node_id, m.last_monitor_time) IN (
|
||||
SELECT m1.standby_node_id, MAX(m1.last_monitor_time)
|
||||
FROM repmgr.monitoring_history m1 GROUP BY 1
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* drop old tables */
|
||||
DROP TABLE repmgr.repl_nodes;
|
||||
DROP TABLE repmgr.repl_monitor;
|
||||
DROP TABLE repmgr.repl_events;
|
||||
|
||||
-- remove temporary table
|
||||
DROP TABLE repmgr_old_schema;
|
||||
@@ -1,557 +0,0 @@
|
||||
/*
|
||||
* repmgr-action-bdr.c
|
||||
*
|
||||
* Implements BDR-related actions for the repmgr command line utility
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
*
|
||||
* 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 "repmgr.h"
|
||||
|
||||
#include "repmgr-client-global.h"
|
||||
#include "repmgr-action-bdr.h"
|
||||
|
||||
|
||||
/*
|
||||
* do_bdr_register()
|
||||
*
|
||||
* As each BDR node is its own primary, registering a BDR node
|
||||
* will create the repmgr metadata schema if necessary.
|
||||
*/
|
||||
void
|
||||
do_bdr_register(void)
|
||||
{
|
||||
PGconn *conn = NULL;
|
||||
BdrNodeInfoList bdr_nodes = T_BDR_NODE_INFO_LIST_INITIALIZER;
|
||||
ExtensionStatus extension_status = REPMGR_UNKNOWN;
|
||||
t_node_info node_info = T_NODE_INFO_INITIALIZER;
|
||||
RecordStatus record_status = RECORD_NOT_FOUND;
|
||||
PQExpBufferData event_details;
|
||||
bool success = true;
|
||||
char *dbname = NULL;
|
||||
|
||||
/* sanity-check configuration for BDR-compatability */
|
||||
if (config_file_options.replication_type != REPLICATION_TYPE_BDR)
|
||||
{
|
||||
log_error(_("cannot run BDR REGISTER on a non-BDR node"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
dbname = pg_malloc0(MAXLEN);
|
||||
|
||||
if (dbname == NULL)
|
||||
{
|
||||
log_error(_("unable to allocate memory; terminating."));
|
||||
exit(ERR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
/* store the database name for future reference */
|
||||
get_conninfo_value(config_file_options.conninfo, "dbname", dbname);
|
||||
|
||||
conn = establish_db_connection(config_file_options.conninfo, true);
|
||||
|
||||
if (!is_bdr_db(conn, NULL))
|
||||
{
|
||||
log_error(_("database \"%s\" is not BDR-enabled"), dbname);
|
||||
log_hint(_("when using repmgr with BDR, the repmgr schema must be stored in the BDR database"));
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/* Check that there are at most 2 BDR nodes */
|
||||
get_all_bdr_node_records(conn, &bdr_nodes);
|
||||
|
||||
if (bdr_nodes.node_count == 0)
|
||||
{
|
||||
log_error(_("database \"%s\" is BDR-enabled but no BDR nodes were found"), dbname);
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/* BDR 2 implementation is for 2 nodes only */
|
||||
if (get_bdr_version_num() < 3 && bdr_nodes.node_count > 2)
|
||||
{
|
||||
log_error(_("repmgr can only support BDR 2.x clusters with 2 nodes"));
|
||||
log_detail(_("this BDR cluster has %i nodes"), bdr_nodes.node_count);
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (get_bdr_version_num() > 2)
|
||||
{
|
||||
log_error(_("\"repmgr bdr register\" is for BDR 2.x only"));
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
|
||||
/* check for a matching BDR node */
|
||||
{
|
||||
PQExpBufferData bdr_local_node_name;
|
||||
bool node_match = false;
|
||||
|
||||
initPQExpBuffer(&bdr_local_node_name);
|
||||
node_match = bdr_node_name_matches(conn, config_file_options.node_name, &bdr_local_node_name);
|
||||
|
||||
if (node_match == false)
|
||||
{
|
||||
if (strlen(bdr_local_node_name.data))
|
||||
{
|
||||
log_error(_("local node BDR node name is \"%s\", expected: \"%s\""),
|
||||
bdr_local_node_name.data,
|
||||
config_file_options.node_name);
|
||||
log_hint(_("\"node_name\" in repmgr.conf must match \"node_name\" in bdr.bdr_nodes"));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error(_("local node does not report BDR node name"));
|
||||
log_hint(_("ensure this is an active BDR node"));
|
||||
}
|
||||
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
termPQExpBuffer(&bdr_local_node_name);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
termPQExpBuffer(&bdr_local_node_name);
|
||||
}
|
||||
|
||||
/* check whether repmgr extension exists, and there are no non-BDR nodes registered */
|
||||
extension_status = get_repmgr_extension_status(conn, NULL);
|
||||
|
||||
if (extension_status == REPMGR_UNKNOWN)
|
||||
{
|
||||
log_error(_("unable to determine status of \"repmgr\" extension in database \"%s\""),
|
||||
dbname);
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (extension_status == REPMGR_UNAVAILABLE)
|
||||
{
|
||||
log_error(_("\"repmgr\" extension is not available"));
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (extension_status == REPMGR_INSTALLED)
|
||||
{
|
||||
if (!is_bdr_repmgr(conn))
|
||||
{
|
||||
log_error(_("repmgr metadatabase contains records for non-BDR nodes"));
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debug("creating repmgr extension in database \"%s\"", dbname);
|
||||
|
||||
begin_transaction(conn);
|
||||
|
||||
if (!create_repmgr_extension(conn))
|
||||
{
|
||||
log_error(_("unable to create repmgr extension - see preceding error message(s); aborting"));
|
||||
rollback_transaction(conn);
|
||||
pfree(dbname);
|
||||
PQfinish(conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
commit_transaction(conn);
|
||||
}
|
||||
|
||||
pfree(dbname);
|
||||
|
||||
if (bdr_node_has_repmgr_set(conn, config_file_options.node_name) == false)
|
||||
{
|
||||
log_debug("bdr_node_has_repmgr_set() = false");
|
||||
bdr_node_set_repmgr_set(conn, config_file_options.node_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* before adding the extension tables to the replication set, if any other
|
||||
* BDR nodes exist, populate repmgr.nodes with a copy of existing entries
|
||||
*
|
||||
* currently we won't copy the contents of any other tables
|
||||
*
|
||||
*/
|
||||
{
|
||||
NodeInfoList local_node_records = T_NODE_INFO_LIST_INITIALIZER;
|
||||
|
||||
(void) get_all_node_records(conn, &local_node_records);
|
||||
|
||||
if (local_node_records.node_count == 0)
|
||||
{
|
||||
BdrNodeInfoList bdr_nodes = T_BDR_NODE_INFO_LIST_INITIALIZER;
|
||||
BdrNodeInfoListCell *bdr_cell = NULL;
|
||||
|
||||
get_all_bdr_node_records(conn, &bdr_nodes);
|
||||
|
||||
if (bdr_nodes.node_count == 0)
|
||||
{
|
||||
log_error(_("unable to retrieve any BDR node records"));
|
||||
log_detail("%s", PQerrorMessage(conn));
|
||||
PQfinish(conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
for (bdr_cell = bdr_nodes.head; bdr_cell; bdr_cell = bdr_cell->next)
|
||||
{
|
||||
PGconn *bdr_node_conn = NULL;
|
||||
NodeInfoList existing_nodes = T_NODE_INFO_LIST_INITIALIZER;
|
||||
NodeInfoListCell *cell = NULL;
|
||||
ExtensionStatus other_node_extension_status = REPMGR_UNKNOWN;
|
||||
|
||||
/* skip the local node */
|
||||
if (strncmp(node_info.node_name, bdr_cell->node_info->node_name, sizeof(node_info.node_name)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("connecting to BDR node \"%s\" (conninfo: \"%s\")",
|
||||
bdr_cell->node_info->node_name,
|
||||
bdr_cell->node_info->node_local_dsn);
|
||||
bdr_node_conn = establish_db_connection_quiet(bdr_cell->node_info->node_local_dsn);
|
||||
|
||||
if (PQstatus(bdr_node_conn) != CONNECTION_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check repmgr schema exists, skip if not */
|
||||
other_node_extension_status = get_repmgr_extension_status(bdr_node_conn, NULL);
|
||||
|
||||
if (other_node_extension_status != REPMGR_INSTALLED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
(void) get_all_node_records(bdr_node_conn, &existing_nodes);
|
||||
|
||||
for (cell = existing_nodes.head; cell; cell = cell->next)
|
||||
{
|
||||
log_debug("creating record for node \"%s\" (ID: %i)",
|
||||
cell->node_info->node_name, cell->node_info->node_id);
|
||||
create_node_record(conn, "bdr register", cell->node_info);
|
||||
}
|
||||
|
||||
PQfinish(bdr_node_conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the repmgr extension tables to a replication set */
|
||||
|
||||
if (get_bdr_version_num() < 3)
|
||||
{
|
||||
add_extension_tables_to_bdr_replication_set(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this is the only table we need to replicate */
|
||||
char *replication_set = get_default_bdr_replication_set(conn);
|
||||
|
||||
/*
|
||||
* this probably won't happen, but we need to be sure we're using
|
||||
* the replication set metadata correctly...
|
||||
*/
|
||||
if (conn == NULL)
|
||||
{
|
||||
log_error(_("unable to retrieve default BDR replication set"));
|
||||
log_hint(_("see preceding messages"));
|
||||
log_debug("check query in get_default_bdr_replication_set()");
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (is_table_in_bdr_replication_set(conn, "nodes", replication_set) == false)
|
||||
{
|
||||
add_table_to_bdr_replication_set(conn, "nodes", replication_set);
|
||||
}
|
||||
|
||||
pfree(replication_set);
|
||||
}
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
begin_transaction(conn);
|
||||
|
||||
/*
|
||||
* we'll check if a record exists (even if the schema was just created),
|
||||
* as there's a faint chance of a race condition
|
||||
*/
|
||||
|
||||
record_status = get_node_record(conn, config_file_options.node_id, &node_info);
|
||||
|
||||
/* Update internal node record */
|
||||
|
||||
node_info.type = BDR;
|
||||
node_info.node_id = config_file_options.node_id;
|
||||
node_info.upstream_node_id = NO_UPSTREAM_NODE;
|
||||
node_info.active = true;
|
||||
node_info.priority = config_file_options.priority;
|
||||
|
||||
strncpy(node_info.node_name, config_file_options.node_name, sizeof(node_info.node_name));
|
||||
strncpy(node_info.location, config_file_options.location, sizeof(node_info.location));
|
||||
strncpy(node_info.conninfo, config_file_options.conninfo, sizeof(node_info.conninfo));
|
||||
|
||||
if (record_status == RECORD_FOUND)
|
||||
{
|
||||
bool node_updated = false;
|
||||
|
||||
/*
|
||||
* At this point we will have established there are no non-BDR
|
||||
* records, so no need to verify the node type
|
||||
*/
|
||||
if (!runtime_options.force)
|
||||
{
|
||||
log_error(_("this node is already registered"));
|
||||
log_hint(_("use -F/--force to overwrite the existing node record"));
|
||||
rollback_transaction(conn);
|
||||
PQfinish(conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* don't permit changing the node name - this must match the BDR node
|
||||
* name set when the node was registered.
|
||||
*/
|
||||
|
||||
if (strncmp(node_info.node_name, config_file_options.node_name, sizeof(node_info.node_name)) != 0)
|
||||
{
|
||||
log_error(_("a record for node %i is already registered with node_name \"%s\""),
|
||||
config_file_options.node_id, node_info.node_name);
|
||||
log_hint(_("node_name configured in repmgr.conf is \"%s\""), config_file_options.node_name);
|
||||
|
||||
rollback_transaction(conn);
|
||||
PQfinish(conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
node_updated = update_node_record(conn, "bdr register", &node_info);
|
||||
|
||||
if (node_updated == true)
|
||||
{
|
||||
appendPQExpBuffer(&event_details, _("node record updated for node \"%s\" (%i)"),
|
||||
config_file_options.node_name, config_file_options.node_id);
|
||||
log_verbose(LOG_NOTICE, "%s", event_details.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* create new node record */
|
||||
bool node_created = create_node_record(conn, "bdr register", &node_info);
|
||||
|
||||
if (node_created == true)
|
||||
{
|
||||
appendPQExpBuffer(&event_details,
|
||||
_("node record created for node \"%s\" (ID: %i)"),
|
||||
config_file_options.node_name, config_file_options.node_id);
|
||||
log_notice("%s", event_details.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success == false)
|
||||
{
|
||||
rollback_transaction(conn);
|
||||
PQfinish(conn);
|
||||
exit(ERR_DB_QUERY);
|
||||
}
|
||||
|
||||
commit_transaction(conn);
|
||||
/* Log the event */
|
||||
create_event_notification(
|
||||
conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"bdr_register",
|
||||
true,
|
||||
event_details.data);
|
||||
|
||||
termPQExpBuffer(&event_details);
|
||||
|
||||
PQfinish(conn);
|
||||
|
||||
log_notice(_("BDR node %i registered (conninfo: %s)"),
|
||||
config_file_options.node_id, config_file_options.conninfo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
do_bdr_unregister(void)
|
||||
{
|
||||
PGconn *conn = NULL;
|
||||
ExtensionStatus extension_status = REPMGR_UNKNOWN;
|
||||
int target_node_id = UNKNOWN_NODE_ID;
|
||||
t_node_info node_info = T_NODE_INFO_INITIALIZER;
|
||||
RecordStatus record_status = RECORD_NOT_FOUND;
|
||||
bool node_record_deleted = false;
|
||||
PQExpBufferData event_details;
|
||||
char *dbname;
|
||||
|
||||
/* sanity-check configuration for BDR-compatability */
|
||||
|
||||
if (config_file_options.replication_type != REPLICATION_TYPE_BDR)
|
||||
{
|
||||
log_error(_("cannot run BDR UNREGISTER on a non-BDR node"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
dbname = pg_malloc0(MAXLEN);
|
||||
|
||||
if (dbname == NULL)
|
||||
{
|
||||
log_error(_("unable to allocate memory; terminating."));
|
||||
exit(ERR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
/* store the database name for future reference */
|
||||
get_conninfo_value(config_file_options.conninfo, "dbname", dbname);
|
||||
|
||||
conn = establish_db_connection(config_file_options.conninfo, true);
|
||||
|
||||
if (!is_bdr_db(conn, NULL))
|
||||
{
|
||||
log_error(_("database \"%s\" is not BDR-enabled"), dbname);
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
extension_status = get_repmgr_extension_status(conn, NULL);
|
||||
if (extension_status != REPMGR_INSTALLED)
|
||||
{
|
||||
log_error(_("repmgr is not installed on database \"%s\""), dbname);
|
||||
PQfinish(conn);
|
||||
pfree(dbname);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
pfree(dbname);
|
||||
|
||||
if (!is_bdr_repmgr(conn))
|
||||
{
|
||||
log_error(_("repmgr metadatabase contains records for non-BDR nodes"));
|
||||
PQfinish(conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
if (runtime_options.node_id != UNKNOWN_NODE_ID)
|
||||
target_node_id = runtime_options.node_id;
|
||||
else
|
||||
target_node_id = config_file_options.node_id;
|
||||
|
||||
|
||||
/* Check node exists and is really a BDR node */
|
||||
record_status = get_node_record(conn, target_node_id, &node_info);
|
||||
|
||||
if (record_status != RECORD_FOUND)
|
||||
{
|
||||
log_error(_("no record found for node %i"), target_node_id);
|
||||
PQfinish(conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
begin_transaction(conn);
|
||||
|
||||
log_debug("unregistering node %i", target_node_id);
|
||||
|
||||
node_record_deleted = delete_node_record(conn, target_node_id);
|
||||
|
||||
if (node_record_deleted == false)
|
||||
{
|
||||
appendPQExpBuffer(&event_details,
|
||||
"unable to delete node record for node \"%s\" (ID: %i)",
|
||||
node_info.node_name,
|
||||
target_node_id);
|
||||
rollback_transaction(conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
appendPQExpBuffer(&event_details,
|
||||
"node record deleted for node \"%s\" (ID: %i)",
|
||||
node_info.node_name,
|
||||
target_node_id);
|
||||
commit_transaction(conn);
|
||||
}
|
||||
|
||||
|
||||
/* Log the event */
|
||||
create_event_notification(
|
||||
conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"bdr_unregister",
|
||||
true,
|
||||
event_details.data);
|
||||
|
||||
PQfinish(conn);
|
||||
|
||||
log_notice(_("bdr node \"%s\" (ID: %i) successfully unregistered"),
|
||||
node_info.node_name, target_node_id);
|
||||
|
||||
termPQExpBuffer(&event_details);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
do_bdr_help(void)
|
||||
{
|
||||
print_help_header();
|
||||
|
||||
printf(_("Usage:\n"));
|
||||
printf(_(" %s [OPTIONS] bdr register\n"), progname());
|
||||
printf(_(" %s [OPTIONS] bdr unregister\n"), progname());
|
||||
puts("");
|
||||
|
||||
printf(_("BDR REGISTER\n"));
|
||||
puts("");
|
||||
printf(_(" \"bdr register\" initialises the repmgr cluster and registers the initial bdr node.\n"));
|
||||
puts("");
|
||||
printf(_(" -F, --force overwrite an existing node record\n"));
|
||||
puts("");
|
||||
|
||||
printf(_("BDR UNREGISTER\n"));
|
||||
puts("");
|
||||
printf(_(" \"bdr unregister\" unregisters an inactive BDR node.\n"));
|
||||
puts("");
|
||||
printf(_(" --node-id ID node to unregister (optional, used when the node to unregister\n" \
|
||||
" is offline)\n"));
|
||||
puts("");
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* repmgr-action-bdr.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
*
|
||||
* 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 _REPMGR_ACTION_BDR_H_
|
||||
#define _REPMGR_ACTION_BDR_H_
|
||||
|
||||
extern void do_bdr_register(void);
|
||||
extern void do_bdr_unregister(void);
|
||||
|
||||
extern void do_bdr_help(void);
|
||||
|
||||
|
||||
#endif /* _REPMGR_ACTION_BDR_H_ */
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Implements cluster information actions for the repmgr command line utility
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -55,10 +55,8 @@ typedef enum
|
||||
struct ColHeader headers_show[SHOW_HEADER_COUNT];
|
||||
struct ColHeader headers_event[EVENT_HEADER_COUNT];
|
||||
|
||||
|
||||
|
||||
static int build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, ItemList *warnings, int *error_code);
|
||||
static int build_cluster_crosscheck(t_node_status_cube ***cube_dest, int *name_length, ItemList *warnings, int *error_code);
|
||||
static int build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, int *error_code);
|
||||
static int build_cluster_crosscheck(t_node_status_cube ***cube_dest, ItemList *warnings, int *error_code);
|
||||
static void cube_set_node_status(t_node_status_cube **cube, int n, int node_id, int matrix_node_id, int connection_node_id, int connection_status);
|
||||
|
||||
/*
|
||||
@@ -67,6 +65,8 @@ static void cube_set_node_status(t_node_status_cube **cube, int n, int node_id,
|
||||
* Parameters:
|
||||
* --compact
|
||||
* --csv
|
||||
* --terse
|
||||
* --verbose
|
||||
*/
|
||||
void
|
||||
do_cluster_show(void)
|
||||
@@ -206,7 +206,8 @@ do_cluster_show(void)
|
||||
else
|
||||
{
|
||||
/* NOP on pre-9.6 servers */
|
||||
cell->node_info->replication_info->timeline_id = get_node_timeline(cell->node_info->conn);
|
||||
cell->node_info->replication_info->timeline_id = get_node_timeline(cell->node_info->conn,
|
||||
cell->node_info->replication_info->timeline_id_str);
|
||||
}
|
||||
|
||||
initPQExpBuffer(&node_status);
|
||||
@@ -244,18 +245,13 @@ do_cluster_show(void)
|
||||
|
||||
headers_show[SHOW_LOCATION].cur_length = strlen(cell->node_info->location);
|
||||
|
||||
if (cell->node_info->replication_info->timeline_id == UNKNOWN_TIMELINE_ID)
|
||||
/* Format timeline ID */
|
||||
if (cell->node_info->type == WITNESS)
|
||||
{
|
||||
/* display "?" */
|
||||
headers_show[SHOW_TIMELINE_ID].cur_length = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
initPQExpBuffer(&buf);
|
||||
appendPQExpBuffer(&buf, "%i", cell->node_info->replication_info->timeline_id);
|
||||
headers_show[SHOW_TIMELINE_ID].cur_length = strlen(buf.data);
|
||||
termPQExpBuffer(&buf);
|
||||
/* The witness node's timeline ID is irrelevant */
|
||||
strncpy(cell->node_info->replication_info->timeline_id_str, _("n/a"), MAXLEN);
|
||||
}
|
||||
headers_show[SHOW_TIMELINE_ID].cur_length = strlen(cell->node_info->replication_info->timeline_id_str);
|
||||
|
||||
headers_show[SHOW_CONNINFO].cur_length = strlen(cell->node_info->conninfo);
|
||||
|
||||
@@ -322,10 +318,7 @@ do_cluster_show(void)
|
||||
|
||||
if (headers_show[SHOW_TIMELINE_ID].display == true)
|
||||
{
|
||||
if (cell->node_info->replication_info->timeline_id == UNKNOWN_TIMELINE_ID)
|
||||
printf("| %-*c ", headers_show[SHOW_TIMELINE_ID].max_length, '?');
|
||||
else
|
||||
printf("| %-*i ", headers_show[SHOW_TIMELINE_ID].max_length, (int)cell->node_info->replication_info->timeline_id);
|
||||
printf("| %-*s ", headers_show[SHOW_TIMELINE_ID].max_length, cell->node_info->replication_info->timeline_id_str);
|
||||
}
|
||||
|
||||
if (headers_show[SHOW_CONNINFO].display == true)
|
||||
@@ -343,14 +336,25 @@ do_cluster_show(void)
|
||||
/* emit any warnings */
|
||||
if (warnings.head != NULL && runtime_options.terse == false && runtime_options.output_mode != OM_CSV)
|
||||
{
|
||||
ItemListCell *cell = NULL;
|
||||
|
||||
printf(_("\nWARNING: following issues were detected\n"));
|
||||
ItemListCell *cell = NULL;
|
||||
PQExpBufferData warning;
|
||||
|
||||
initPQExpBuffer(&warning);
|
||||
|
||||
appendPQExpBufferStr(&warning,
|
||||
_("following issues were detected\n"));
|
||||
for (cell = warnings.head; cell; cell = cell->next)
|
||||
{
|
||||
printf(_(" - %s\n"), cell->string);
|
||||
appendPQExpBuffer(&warning,
|
||||
_(" - %s\n"), cell->string);
|
||||
}
|
||||
|
||||
puts("");
|
||||
log_warning("%s", warning.data);
|
||||
|
||||
termPQExpBuffer(&warning);
|
||||
|
||||
if (runtime_options.verbose == false && connection_error_found == true)
|
||||
{
|
||||
log_hint(_("execute with --verbose option to see connection error messages"));
|
||||
@@ -532,9 +536,6 @@ do_cluster_crosscheck(void)
|
||||
{
|
||||
int i = 0,
|
||||
n = 0;
|
||||
char c;
|
||||
const char *node_header = "Name";
|
||||
int name_length = strlen(node_header);
|
||||
|
||||
t_node_status_cube **cube;
|
||||
|
||||
@@ -542,7 +543,7 @@ do_cluster_crosscheck(void)
|
||||
int error_code = SUCCESS;
|
||||
ItemList warnings = {NULL, NULL};
|
||||
|
||||
n = build_cluster_crosscheck(&cube, &name_length, &warnings, &error_code);
|
||||
n = build_cluster_crosscheck(&cube, &warnings, &error_code);
|
||||
|
||||
if (runtime_options.output_mode == OM_CSV)
|
||||
{
|
||||
@@ -576,24 +577,56 @@ do_cluster_crosscheck(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%*s | Id ", name_length, node_header);
|
||||
for (i = 0; i < n; i++)
|
||||
printf("| %2d ", cube[i]->node_id);
|
||||
printf("\n");
|
||||
/* output header contains node name, node ID and one column for each node in the cluster */
|
||||
struct ColHeader *headers_crosscheck = NULL;
|
||||
|
||||
int header_count = n + 2;
|
||||
int header_id = 2;
|
||||
|
||||
headers_crosscheck = palloc0(sizeof(ColHeader) * header_count);
|
||||
|
||||
/* Initialize column headers */
|
||||
strncpy(headers_crosscheck[0].title, _("Name"), MAXLEN);
|
||||
strncpy(headers_crosscheck[1].title, _("ID"), MAXLEN);
|
||||
|
||||
for (i = 0; i < name_length; i++)
|
||||
printf("-");
|
||||
printf("-+----");
|
||||
for (i = 0; i < n; i++)
|
||||
printf("+----");
|
||||
printf("\n");
|
||||
{
|
||||
maxlen_snprintf(headers_crosscheck[header_id].title, "%i", cube[i]->node_id);
|
||||
header_id++;
|
||||
}
|
||||
|
||||
/* Initialize column max values */
|
||||
for (i = 0; i < header_count; i++)
|
||||
{
|
||||
headers_crosscheck[i].display = true;
|
||||
headers_crosscheck[i].max_length = strlen(headers_crosscheck[i].title);
|
||||
headers_crosscheck[i].cur_length = headers_crosscheck[i].max_length;
|
||||
|
||||
/* We can derive the maximum node ID length for the ID column from
|
||||
* the generated matrix node ID headers
|
||||
*/
|
||||
if (i >= 2 && headers_crosscheck[i].max_length > headers_crosscheck[1].max_length)
|
||||
headers_crosscheck[1].max_length = headers_crosscheck[i].max_length;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (strlen(cube[i]->node_name) > headers_crosscheck[0].max_length)
|
||||
{
|
||||
headers_crosscheck[0].max_length = strlen(cube[i]->node_name);
|
||||
}
|
||||
}
|
||||
|
||||
print_status_header(header_count, headers_crosscheck);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
int column_node_ix;
|
||||
|
||||
printf("%*s | %2d ", name_length,
|
||||
printf(" %-*s | %-*i ",
|
||||
headers_crosscheck[0].max_length,
|
||||
cube[i]->node_name,
|
||||
headers_crosscheck[1].max_length,
|
||||
cube[i]->node_id);
|
||||
|
||||
for (column_node_ix = 0; column_node_ix < n; column_node_ix++)
|
||||
@@ -601,6 +634,8 @@ do_cluster_crosscheck(void)
|
||||
int max_node_status = -2;
|
||||
int node_ix = 0;
|
||||
|
||||
char c;
|
||||
|
||||
/*
|
||||
* The value of entry (i,j) is equal to the maximum value of all
|
||||
* the (i,j,k). Indeed:
|
||||
@@ -640,12 +675,14 @@ do_cluster_crosscheck(void)
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
printf("| %c ", c);
|
||||
printf("| %-*c ", headers_crosscheck[column_node_ix + 2].max_length, c);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
pfree(headers_crosscheck);
|
||||
|
||||
if (warnings.head != NULL && runtime_options.terse == false)
|
||||
{
|
||||
log_warning(_("following problems detected:"));
|
||||
@@ -702,16 +739,13 @@ do_cluster_matrix()
|
||||
j = 0,
|
||||
n = 0;
|
||||
|
||||
const char *node_header = "Name";
|
||||
int name_length = strlen(node_header);
|
||||
|
||||
t_node_matrix_rec **matrix_rec_list;
|
||||
|
||||
bool connection_error_found = false;
|
||||
int error_code = SUCCESS;
|
||||
ItemList warnings = {NULL, NULL};
|
||||
|
||||
n = build_cluster_matrix(&matrix_rec_list, &name_length, &warnings, &error_code);
|
||||
n = build_cluster_matrix(&matrix_rec_list, &warnings, &error_code);
|
||||
|
||||
if (runtime_options.output_mode == OM_CSV)
|
||||
{
|
||||
@@ -734,27 +768,60 @@ do_cluster_matrix()
|
||||
}
|
||||
else
|
||||
{
|
||||
char c;
|
||||
/* output header contains node name, node ID and one column for each node in the cluster */
|
||||
struct ColHeader *headers_matrix = NULL;
|
||||
|
||||
printf("%*s | Id ", name_length, node_header);
|
||||
for (i = 0; i < n; i++)
|
||||
printf("| %2d ", matrix_rec_list[i]->node_id);
|
||||
printf("\n");
|
||||
int header_count = n + 2;
|
||||
int header_id = 2;
|
||||
|
||||
for (i = 0; i < name_length; i++)
|
||||
printf("-");
|
||||
printf("-+----");
|
||||
for (i = 0; i < n; i++)
|
||||
printf("+----");
|
||||
printf("\n");
|
||||
headers_matrix = palloc0(sizeof(ColHeader) * header_count);
|
||||
|
||||
/* Initialize column headers */
|
||||
strncpy(headers_matrix[0].title, _("Name"), MAXLEN);
|
||||
strncpy(headers_matrix[1].title, _("ID"), MAXLEN);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
printf("%*s | %2d ", name_length,
|
||||
maxlen_snprintf(headers_matrix[header_id].title, "%i", matrix_rec_list[i]->node_id);
|
||||
header_id++;
|
||||
}
|
||||
|
||||
/* Initialize column max values */
|
||||
for (i = 0; i < header_count; i++)
|
||||
{
|
||||
headers_matrix[i].display = true;
|
||||
headers_matrix[i].max_length = strlen(headers_matrix[i].title);
|
||||
headers_matrix[i].cur_length = headers_matrix[i].max_length;
|
||||
|
||||
/* We can derive the maximum node ID length for the ID column from
|
||||
* the generated matrix node ID headers
|
||||
*/
|
||||
if (i >= 2 && headers_matrix[i].max_length > headers_matrix[1].max_length)
|
||||
headers_matrix[1].max_length = headers_matrix[i].max_length;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (strlen(matrix_rec_list[i]->node_name) > headers_matrix[0].max_length)
|
||||
{
|
||||
headers_matrix[0].max_length = strlen(matrix_rec_list[i]->node_name);
|
||||
}
|
||||
}
|
||||
|
||||
print_status_header(header_count, headers_matrix);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
printf(" %-*s | %-*i ",
|
||||
headers_matrix[0].max_length,
|
||||
matrix_rec_list[i]->node_name,
|
||||
headers_matrix[1].max_length,
|
||||
matrix_rec_list[i]->node_id);
|
||||
for (j = 0; j < n; j++)
|
||||
{
|
||||
char c;
|
||||
|
||||
switch (matrix_rec_list[i]->node_status_list[j]->node_status)
|
||||
{
|
||||
case -2:
|
||||
@@ -772,11 +839,13 @@ do_cluster_matrix()
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
printf("| %c ", c);
|
||||
printf("| %-*c ", headers_matrix[j + 2].max_length, c);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
pfree(headers_matrix);
|
||||
|
||||
if (warnings.head != NULL && runtime_options.terse == false)
|
||||
{
|
||||
log_warning(_("following problems detected:"));
|
||||
@@ -832,7 +901,7 @@ matrix_set_node_status(t_node_matrix_rec **matrix_rec_list, int n, int node_id,
|
||||
|
||||
|
||||
static int
|
||||
build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, ItemList *warnings, int *error_code)
|
||||
build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, ItemList *warnings, int *error_code)
|
||||
{
|
||||
PGconn *conn = NULL;
|
||||
int i = 0,
|
||||
@@ -890,7 +959,6 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, Ite
|
||||
/* Initialise matrix structure for each node */
|
||||
for (cell = nodes.head; cell; cell = cell->next)
|
||||
{
|
||||
int name_length_cur;
|
||||
NodeInfoListCell *cell_j;
|
||||
|
||||
matrix_rec_list[i] = (t_node_matrix_rec *) pg_malloc0(sizeof(t_node_matrix_rec));
|
||||
@@ -900,13 +968,6 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, Ite
|
||||
cell->node_info->node_name,
|
||||
sizeof(matrix_rec_list[i]->node_name));
|
||||
|
||||
/*
|
||||
* Find the maximum length of a node name
|
||||
*/
|
||||
name_length_cur = strlen(matrix_rec_list[i]->node_name);
|
||||
if (name_length_cur > *name_length)
|
||||
*name_length = name_length_cur;
|
||||
|
||||
matrix_rec_list[i]->node_status_list = (t_node_status_rec **) pg_malloc0(sizeof(t_node_status_rec) * nodes.node_count);
|
||||
|
||||
j = 0;
|
||||
@@ -1071,7 +1132,7 @@ build_cluster_matrix(t_node_matrix_rec ***matrix_rec_dest, int *name_length, Ite
|
||||
|
||||
|
||||
static int
|
||||
build_cluster_crosscheck(t_node_status_cube ***dest_cube, int *name_length, ItemList *warnings, int *error_code)
|
||||
build_cluster_crosscheck(t_node_status_cube ***dest_cube, ItemList *warnings, int *error_code)
|
||||
{
|
||||
PGconn *conn = NULL;
|
||||
int h,
|
||||
@@ -1120,20 +1181,12 @@ build_cluster_crosscheck(t_node_status_cube ***dest_cube, int *name_length, Item
|
||||
|
||||
for (cell = nodes.head; cell; cell = cell->next)
|
||||
{
|
||||
int name_length_cur = 0;
|
||||
NodeInfoListCell *cell_i = NULL;
|
||||
|
||||
cube[h] = (t_node_status_cube *) pg_malloc(sizeof(t_node_status_cube));
|
||||
cube[h]->node_id = cell->node_info->node_id;
|
||||
strncpy(cube[h]->node_name, cell->node_info->node_name, sizeof(cube[h]->node_name));
|
||||
|
||||
/*
|
||||
* Find the maximum length of a node name
|
||||
*/
|
||||
name_length_cur = strlen(cube[h]->node_name);
|
||||
if (name_length_cur > *name_length)
|
||||
*name_length = name_length_cur;
|
||||
|
||||
cube[h]->matrix_list_rec = (t_node_matrix_rec **) pg_malloc(sizeof(t_node_matrix_rec) * nodes.node_count);
|
||||
|
||||
i = 0;
|
||||
@@ -1396,6 +1449,10 @@ do_cluster_cleanup(void)
|
||||
log_warning(_("unable to vacuum table \"repmgr.monitoring_history\""));
|
||||
log_detail("%s", PQerrorMessage(primary_conn));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_info(_("vacuum of table \"repmgr.monitoring_history\" completed"));
|
||||
}
|
||||
|
||||
if (runtime_options.keep_history == 0)
|
||||
{
|
||||
@@ -1497,4 +1554,5 @@ do_cluster_help(void)
|
||||
printf(_(" -k, --keep-history=VALUE retain indicated number of days of history (default: 0)\n"));
|
||||
puts("");
|
||||
|
||||
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-cluster.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* repmgr-action-daemon.c
|
||||
*
|
||||
* Implements repmgrd actions for the repmgr command line utility
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -339,4 +339,5 @@ void do_daemon_help(void)
|
||||
|
||||
puts("");
|
||||
|
||||
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-daemon.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-node.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Implements primary actions for the repmgr command line utility
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -454,9 +454,9 @@ do_primary_unregister(void)
|
||||
|
||||
/*
|
||||
* This appears to be the cluster primary - cowardly refuse to
|
||||
* delete the record
|
||||
* delete the record, unless --force is supplied.
|
||||
*/
|
||||
if (primary_node_info.node_id == target_node_info_ptr->node_id)
|
||||
if (primary_node_info.node_id == target_node_info_ptr->node_id && !runtime_options.force)
|
||||
{
|
||||
log_error(_("node \"%s\" (ID: %i) is the current primary node, unable to unregister"),
|
||||
target_node_info_ptr->node_name,
|
||||
@@ -575,4 +575,5 @@ do_primary_help(void)
|
||||
|
||||
puts("");
|
||||
|
||||
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-primary.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* repmgr-action-service.c
|
||||
*
|
||||
* Implements repmgrd actions for the repmgr command line utility
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -357,11 +357,22 @@ do_service_status(void)
|
||||
{
|
||||
ItemListCell *cell = NULL;
|
||||
|
||||
printf(_("\nWARNING: following issues were detected\n"));
|
||||
PQExpBufferData warning;
|
||||
|
||||
initPQExpBuffer(&warning);
|
||||
|
||||
appendPQExpBufferStr(&warning,
|
||||
_("following issues were detected\n"));
|
||||
|
||||
for (cell = warnings.head; cell; cell = cell->next)
|
||||
{
|
||||
printf(_(" - %s\n"), cell->string);
|
||||
appendPQExpBuffer(&warning,
|
||||
_(" - %s\n"), cell->string);
|
||||
}
|
||||
puts("");
|
||||
log_warning("%s", warning.data);
|
||||
|
||||
termPQExpBuffer(&warning);
|
||||
|
||||
if (runtime_options.verbose == false && connection_error_found == true)
|
||||
{
|
||||
@@ -532,4 +543,6 @@ void do_service_help(void)
|
||||
puts("");
|
||||
|
||||
puts("");
|
||||
|
||||
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-service.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-standby.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Implements witness actions for the repmgr command line utility
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -74,18 +74,6 @@ do_witness_register(void)
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/* check that witness node is not a BDR node */
|
||||
if (is_bdr_db_quiet(witness_conn) == true)
|
||||
{
|
||||
log_error(_("witness node is a BDR node"));
|
||||
log_hint(_("a witness node cannot be configured for a BDR cluster"));
|
||||
|
||||
PQfinish(witness_conn);
|
||||
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
|
||||
/* connect to primary with provided parameters */
|
||||
log_info(_("connecting to primary node"));
|
||||
|
||||
@@ -194,19 +182,6 @@ do_witness_register(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* check that primary node is not a BDR node */
|
||||
if (is_bdr_db_quiet(primary_conn) == true)
|
||||
{
|
||||
log_error(_("primary node is a BDR node"));
|
||||
log_hint(_("a witness node cannot be configured for a BDR cluster"));
|
||||
|
||||
PQfinish(witness_conn);
|
||||
PQfinish(primary_conn);
|
||||
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
|
||||
/* create repmgr extension, if does not exist */
|
||||
if (runtime_options.dry_run == false && !create_repmgr_extension(witness_conn))
|
||||
{
|
||||
@@ -594,5 +569,5 @@ void do_witness_help(void)
|
||||
|
||||
puts("");
|
||||
|
||||
return;
|
||||
printf(_("%s home page: <%s>\n"), "repmgr", REPMGR_URL);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-action-witness.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-client-global.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -28,6 +28,8 @@
|
||||
/* default value for "cluster event --limit"*/
|
||||
#define CLUSTER_EVENT_LIMIT 20
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* configuration metadata */
|
||||
@@ -46,6 +48,7 @@ typedef struct
|
||||
bool no_wait;
|
||||
bool compact;
|
||||
bool detail;
|
||||
bool dump_config;
|
||||
|
||||
/* logging options */
|
||||
char log_level[MAXLEN]; /* overrides setting in repmgr.conf */
|
||||
@@ -85,7 +88,8 @@ typedef struct
|
||||
char replication_user[MAXLEN];
|
||||
char upstream_conninfo[MAXLEN];
|
||||
bool without_barman;
|
||||
bool recovery_conf_only;
|
||||
bool replication_conf_only;
|
||||
bool verify_backup;
|
||||
|
||||
/* "standby clone"/"standby follow" options */
|
||||
int upstream_node_id;
|
||||
@@ -109,6 +113,7 @@ typedef struct
|
||||
/* "node check" options */
|
||||
bool archive_ready;
|
||||
bool downstream;
|
||||
bool upstream;
|
||||
bool replication_lag;
|
||||
bool role;
|
||||
bool slots;
|
||||
@@ -116,6 +121,8 @@ typedef struct
|
||||
bool has_passfile;
|
||||
bool replication_connection;
|
||||
bool data_directory_config;
|
||||
bool replication_config_owner;
|
||||
bool db_connection;
|
||||
|
||||
/* "node rejoin" options */
|
||||
char config_files[MAXLEN];
|
||||
@@ -136,7 +143,7 @@ typedef struct
|
||||
|
||||
/* following options for internal use */
|
||||
char config_archive_dir[MAXPGPATH];
|
||||
OutputMode output_mode;
|
||||
OutputMode output_mode; /* set through provision of --csv, --nagios or --optformat */
|
||||
bool disable_wal_receiver;
|
||||
bool enable_wal_receiver;
|
||||
} t_runtime_options;
|
||||
@@ -145,7 +152,7 @@ typedef struct
|
||||
/* configuration metadata */ \
|
||||
false, false, false, false, false, \
|
||||
/* general configuration options */ \
|
||||
"", false, false, "", -1, false, false, false, \
|
||||
"", false, false, "", -1, false, false, false, false, \
|
||||
/* logging options */ \
|
||||
"", false, false, false, false, \
|
||||
/* output options */ \
|
||||
@@ -158,7 +165,7 @@ typedef struct
|
||||
UNKNOWN_NODE_ID, "", "", UNKNOWN_NODE_ID, \
|
||||
/* "standby clone" options */ \
|
||||
false, CONFIG_FILE_SAMEPATH, false, false, false, "", "", "", \
|
||||
false, false, \
|
||||
false, false, false, \
|
||||
/* "standby clone"/"standby follow" options */ \
|
||||
NO_UPSTREAM_NODE, \
|
||||
/* "standby register" options */ \
|
||||
@@ -168,7 +175,7 @@ typedef struct
|
||||
/* "node status" options */ \
|
||||
false, \
|
||||
/* "node check" options */ \
|
||||
false, false, false, false, false, false, false, false, false, \
|
||||
false, false, false, false, false, false, false, false, false, false, false, false, \
|
||||
/* "node rejoin" options */ \
|
||||
"", \
|
||||
/* "node service" options */ \
|
||||
@@ -201,6 +208,30 @@ typedef enum
|
||||
} t_server_action;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
USER_TYPE_UNKNOWN = -1,
|
||||
REPMGR_USER,
|
||||
REPLICATION_USER_OPT,
|
||||
REPLICATION_USER_NODE,
|
||||
SUPERUSER
|
||||
} t_user_type;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
JOIN_SUCCESS,
|
||||
JOIN_FAIL_NO_PING,
|
||||
JOIN_FAIL_NO_REPLICATION
|
||||
} standy_join_status;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
REMOTE_ERROR_UNKNOWN = -1,
|
||||
REMOTE_ERROR_NONE,
|
||||
REMOTE_ERROR_DB_CONNECTION,
|
||||
REMOTE_ERROR_CONNINFO_PARSE
|
||||
} t_remote_error_type;
|
||||
|
||||
typedef struct ColHeader
|
||||
{
|
||||
char title[MAXLEN];
|
||||
@@ -211,21 +242,17 @@ typedef struct ColHeader
|
||||
|
||||
|
||||
|
||||
/* global configuration structures */
|
||||
/* globally available configuration structures */
|
||||
extern t_runtime_options runtime_options;
|
||||
extern t_configuration_options config_file_options;
|
||||
|
||||
t_conninfo_param_list source_conninfo;
|
||||
|
||||
extern t_conninfo_param_list source_conninfo;
|
||||
extern t_node_info target_node_info;
|
||||
|
||||
/* global variables */
|
||||
extern bool config_file_required;
|
||||
extern char pg_bindir[MAXLEN];
|
||||
|
||||
extern t_node_info target_node_info;
|
||||
|
||||
|
||||
/* global functions */
|
||||
extern int check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string);
|
||||
extern void check_93_config(void);
|
||||
extern bool create_repmgr_extension(PGconn *conn);
|
||||
extern int test_ssh_connection(char *host, char *remote_user);
|
||||
|
||||
@@ -236,7 +263,7 @@ extern int copy_remote_files(char *host, char *remote_user, char *remote_path,
|
||||
|
||||
extern void print_error_list(ItemList *error_list, int log_level);
|
||||
|
||||
extern char *make_pg_path(const char *file);
|
||||
extern void make_pg_path(PQExpBufferData *buf, const char *file);
|
||||
|
||||
extern void get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGconn **privileged_conn);
|
||||
|
||||
@@ -255,9 +282,17 @@ extern void get_node_config_directory(char *config_dir_buf);
|
||||
extern void get_node_data_directory(char *data_dir_buf);
|
||||
extern void init_node_record(t_node_info *node_record);
|
||||
extern bool can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *reason);
|
||||
extern void drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name);
|
||||
extern void make_standby_signal_path(char *buf);
|
||||
extern bool write_standby_signal(void);
|
||||
|
||||
extern bool create_replication_slot(PGconn *conn, char *slot_name, t_node_info *upstream_node_record, PQExpBufferData *error_msg);
|
||||
extern bool drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name);
|
||||
|
||||
extern standy_join_status check_standby_join(PGconn *primary_conn, t_node_info *primary_node_record, t_node_info *standby_node_record);
|
||||
extern bool check_replication_slots_available(int node_id, PGconn* conn);
|
||||
extern bool check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *follow_target_conn, t_node_info *follow_target_node_record, bool is_rejoin);
|
||||
extern bool check_replication_config_owner(int pg_version, const char *data_directory, PQExpBufferData *error_msg, PQExpBufferData *detail_msg);
|
||||
|
||||
extern void check_shared_library(PGconn *conn);
|
||||
extern bool is_repmgrd_running(PGconn *conn);
|
||||
extern int parse_repmgr_version(const char *version_string);
|
||||
|
||||
976
repmgr-client.c
976
repmgr-client.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr-client.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -34,23 +34,21 @@
|
||||
#define STANDBY_SWITCHOVER 8
|
||||
#define WITNESS_REGISTER 9
|
||||
#define WITNESS_UNREGISTER 10
|
||||
#define BDR_REGISTER 11
|
||||
#define BDR_UNREGISTER 12
|
||||
#define NODE_STATUS 13
|
||||
#define NODE_CHECK 14
|
||||
#define NODE_SERVICE 15
|
||||
#define NODE_REJOIN 16
|
||||
#define NODE_CONTROL 17
|
||||
#define CLUSTER_SHOW 18
|
||||
#define CLUSTER_CLEANUP 19
|
||||
#define CLUSTER_MATRIX 20
|
||||
#define CLUSTER_CROSSCHECK 21
|
||||
#define CLUSTER_EVENT 22
|
||||
#define SERVICE_STATUS 23
|
||||
#define SERVICE_PAUSE 24
|
||||
#define SERVICE_UNPAUSE 25
|
||||
#define DAEMON_START 26
|
||||
#define DAEMON_STOP 27
|
||||
#define NODE_STATUS 11
|
||||
#define NODE_CHECK 12
|
||||
#define NODE_SERVICE 13
|
||||
#define NODE_REJOIN 14
|
||||
#define NODE_CONTROL 15
|
||||
#define CLUSTER_SHOW 16
|
||||
#define CLUSTER_CLEANUP 17
|
||||
#define CLUSTER_MATRIX 18
|
||||
#define CLUSTER_CROSSCHECK 19
|
||||
#define CLUSTER_EVENT 20
|
||||
#define SERVICE_STATUS 21
|
||||
#define SERVICE_PAUSE 22
|
||||
#define SERVICE_UNPAUSE 23
|
||||
#define DAEMON_START 24
|
||||
#define DAEMON_STOP 25
|
||||
|
||||
/* command line options without short versions */
|
||||
#define OPT_HELP 1001
|
||||
@@ -83,27 +81,33 @@
|
||||
#define OPT_SIBLINGS_FOLLOW 1028
|
||||
#define OPT_ROLE 1029
|
||||
#define OPT_DOWNSTREAM 1030
|
||||
#define OPT_SLOTS 1031
|
||||
#define OPT_CONFIG_ARCHIVE_DIR 1032
|
||||
#define OPT_UPSTREAM 1031
|
||||
#define OPT_SLOTS 1032
|
||||
#define OPT_HAS_PASSFILE 1033
|
||||
#define OPT_WAIT_START 1034
|
||||
#define OPT_REPL_CONN 1035
|
||||
#define OPT_REMOTE_NODE_ID 1036
|
||||
#define OPT_RECOVERY_CONF_ONLY 1037
|
||||
#define OPT_REPLICATION_CONF_ONLY 1037
|
||||
#define OPT_NO_WAIT 1038
|
||||
#define OPT_MISSING_SLOTS 1039
|
||||
#define OPT_REPMGRD_NO_PAUSE 1040
|
||||
#define OPT_VERSION_NUMBER 1041
|
||||
#define OPT_DATA_DIRECTORY_CONFIG 1042
|
||||
#define OPT_COMPACT 1043
|
||||
#define OPT_DISABLE_WAL_RECEIVER 1044
|
||||
#define OPT_ENABLE_WAL_RECEIVER 1045
|
||||
#define OPT_DETAIL 1046
|
||||
#define OPT_REPMGRD_FORCE_UNPAUSE 1047
|
||||
#define OPT_DETAIL 1044
|
||||
#define OPT_REPMGRD_FORCE_UNPAUSE 1045
|
||||
#define OPT_REPLICATION_CONFIG_OWNER 1046
|
||||
#define OPT_DB_CONNECTION 1047
|
||||
#define OPT_VERIFY_BACKUP 1048
|
||||
|
||||
/* These options are for internal use only */
|
||||
#define OPT_CONFIG_ARCHIVE_DIR 2001
|
||||
#define OPT_DISABLE_WAL_RECEIVER 2002
|
||||
#define OPT_ENABLE_WAL_RECEIVER 2003
|
||||
#define OPT_DUMP_CONFIG 2004
|
||||
|
||||
/* deprecated since 4.0 */
|
||||
#define OPT_CHECK_UPSTREAM_CONFIG 999
|
||||
#define OPT_NODE 998
|
||||
|
||||
|
||||
static struct option long_options[] =
|
||||
@@ -122,6 +126,7 @@ static struct option long_options[] =
|
||||
{"no-wait", no_argument, NULL, 'W'},
|
||||
{"compact", no_argument, NULL, OPT_COMPACT},
|
||||
{"detail", no_argument, NULL, OPT_DETAIL},
|
||||
{"dump-config", no_argument, NULL, OPT_DUMP_CONFIG},
|
||||
|
||||
/* connection options */
|
||||
{"dbname", required_argument, NULL, 'd'},
|
||||
@@ -157,7 +162,10 @@ static struct option long_options[] =
|
||||
{"upstream-conninfo", required_argument, NULL, OPT_UPSTREAM_CONNINFO},
|
||||
{"upstream-node-id", required_argument, NULL, OPT_UPSTREAM_NODE_ID},
|
||||
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
||||
{"recovery-conf-only", no_argument, NULL, OPT_RECOVERY_CONF_ONLY},
|
||||
{"replication-conf-only", no_argument, NULL, OPT_REPLICATION_CONF_ONLY},
|
||||
{"verify-backup", no_argument, NULL, OPT_VERIFY_BACKUP },
|
||||
/* deprecate this once Pg11 and earlier are unsupported */
|
||||
{"recovery-conf-only", no_argument, NULL, OPT_REPLICATION_CONF_ONLY},
|
||||
|
||||
/* "standby register" options */
|
||||
{"wait-start", required_argument, NULL, OPT_WAIT_START},
|
||||
@@ -178,6 +186,7 @@ static struct option long_options[] =
|
||||
/* "node check" options */
|
||||
{"archive-ready", no_argument, NULL, OPT_ARCHIVE_READY},
|
||||
{"downstream", no_argument, NULL, OPT_DOWNSTREAM},
|
||||
{"upstream", no_argument, NULL, OPT_UPSTREAM},
|
||||
{"replication-lag", no_argument, NULL, OPT_REPLICATION_LAG},
|
||||
{"role", no_argument, NULL, OPT_ROLE},
|
||||
{"slots", no_argument, NULL, OPT_SLOTS},
|
||||
@@ -185,6 +194,8 @@ static struct option long_options[] =
|
||||
{"has-passfile", no_argument, NULL, OPT_HAS_PASSFILE},
|
||||
{"replication-connection", no_argument, NULL, OPT_REPL_CONN},
|
||||
{"data-directory-config", no_argument, NULL, OPT_DATA_DIRECTORY_CONFIG},
|
||||
{"replication-config-owner", no_argument, NULL, OPT_REPLICATION_CONFIG_OWNER},
|
||||
{"db-connection", no_argument, NULL, OPT_DB_CONNECTION},
|
||||
|
||||
/* "node rejoin" options */
|
||||
{"config-files", required_argument, NULL, OPT_CONFIG_FILES},
|
||||
@@ -212,9 +223,6 @@ static struct option long_options[] =
|
||||
{"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG},
|
||||
/* previously used by "standby switchover" */
|
||||
{"remote-config-file", required_argument, NULL, 'C'},
|
||||
/* replaced by --node-id */
|
||||
{"node", required_argument, NULL, OPT_NODE},
|
||||
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
79
repmgr.c
79
repmgr.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* repmgr.c - repmgr extension
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* This is the actual extension code; see repmgr-client.c for the code which
|
||||
* generates the repmgr binary
|
||||
@@ -33,22 +33,14 @@
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/spin.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
#if (PG_VERSION_NUM >= 90400)
|
||||
#include "utils/pg_lsn.h"
|
||||
#endif
|
||||
|
||||
#include "utils/timestamp.h"
|
||||
|
||||
#include "lib/stringinfo.h"
|
||||
#include "access/xact.h"
|
||||
#include "utils/snapmgr.h"
|
||||
|
||||
#if (PG_VERSION_NUM >= 90400)
|
||||
#include "pgstat.h"
|
||||
#else
|
||||
#define PGSTAT_STAT_PERMANENT_DIRECTORY "pg_stat"
|
||||
#endif
|
||||
|
||||
#include "voting.h"
|
||||
|
||||
@@ -84,8 +76,6 @@ typedef struct repmgrdSharedState
|
||||
int current_electoral_term;
|
||||
int candidate_node_id;
|
||||
bool follow_new_primary;
|
||||
/* BDR failover */
|
||||
int bdr_failover_handler;
|
||||
} repmgrdSharedState;
|
||||
|
||||
static repmgrdSharedState *shared_state = NULL;
|
||||
@@ -131,12 +121,6 @@ PG_FUNCTION_INFO_V1(get_new_primary);
|
||||
Datum reset_voting_status(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(reset_voting_status);
|
||||
|
||||
Datum am_bdr_failover_handler(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(am_bdr_failover_handler);
|
||||
|
||||
Datum unset_bdr_failover_handler(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(unset_bdr_failover_handler);
|
||||
|
||||
Datum set_repmgrd_pid(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(set_repmgrd_pid);
|
||||
|
||||
@@ -165,8 +149,6 @@ PG_FUNCTION_INFO_V1(get_wal_receiver_pid);
|
||||
void
|
||||
_PG_init(void)
|
||||
{
|
||||
elog(DEBUG1, "repmgr init");
|
||||
|
||||
if (!process_shared_preload_libraries_in_progress)
|
||||
return;
|
||||
|
||||
@@ -183,7 +165,6 @@ _PG_init(void)
|
||||
*/
|
||||
prev_shmem_startup_hook = shmem_startup_hook;
|
||||
shmem_startup_hook = repmgr_shmem_startup;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +222,6 @@ repmgr_shmem_startup(void)
|
||||
shared_state->voting_status = VS_NO_VOTE;
|
||||
shared_state->candidate_node_id = UNKNOWN_NODE_ID;
|
||||
shared_state->follow_new_primary = false;
|
||||
shared_state->bdr_failover_handler = UNKNOWN_NODE_ID;
|
||||
}
|
||||
|
||||
LWLockRelease(AddinShmemInitLock);
|
||||
@@ -571,63 +551,6 @@ reset_voting_status(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
am_bdr_failover_handler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int node_id = UNKNOWN_NODE_ID;
|
||||
bool am_handler = false;
|
||||
|
||||
if (!shared_state)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
node_id = PG_GETARG_INT32(0);
|
||||
|
||||
LWLockAcquire(shared_state->lock, LW_SHARED);
|
||||
|
||||
if (shared_state->bdr_failover_handler == UNKNOWN_NODE_ID)
|
||||
{
|
||||
LWLockRelease(shared_state->lock);
|
||||
LWLockAcquire(shared_state->lock, LW_EXCLUSIVE);
|
||||
shared_state->bdr_failover_handler = node_id;
|
||||
am_handler = true;
|
||||
}
|
||||
else if (shared_state->bdr_failover_handler == node_id)
|
||||
{
|
||||
am_handler = true;
|
||||
}
|
||||
|
||||
LWLockRelease(shared_state->lock);
|
||||
|
||||
PG_RETURN_BOOL(am_handler);
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
unset_bdr_failover_handler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (!shared_state)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
LWLockAcquire(shared_state->lock, LW_SHARED);
|
||||
|
||||
/* only do something if local_node_id is initialised */
|
||||
if (shared_state->local_node_id != UNKNOWN_NODE_ID)
|
||||
{
|
||||
LWLockRelease(shared_state->lock);
|
||||
LWLockAcquire(shared_state->lock, LW_EXCLUSIVE);
|
||||
|
||||
shared_state->bdr_failover_handler = UNKNOWN_NODE_ID;
|
||||
}
|
||||
|
||||
LWLockRelease(shared_state->lock);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the repmgrd pid; or NULL if none set; or -1 if set but repmgrd
|
||||
* process not running (TODO!)
|
||||
|
||||
@@ -71,8 +71,7 @@
|
||||
#replication_user='repmgr' # User to make replication connections with, if not set
|
||||
# defaults to the user defined in "conninfo".
|
||||
|
||||
#replication_type='physical' # Must be one of "physical" or "bdr".
|
||||
# NOTE: "bdr" can only be used with BDR 2.x
|
||||
#replication_type='physical' # Must "physical" (the default).
|
||||
|
||||
#location='default' # An arbitrary string defining the location of the node; this
|
||||
# is used during failover to check visibility of the
|
||||
@@ -182,6 +181,8 @@
|
||||
|
||||
#pg_ctl_options='' # Options to append to "pg_ctl"
|
||||
#pg_basebackup_options='' # Options to append to "pg_basebackup"
|
||||
# (Note: when cloning from Barman, repmgr will honour any
|
||||
# --waldir/--xlogdir setting present in "pg_basebackup_options"
|
||||
#rsync_options='' # Options to append to "rsync"
|
||||
ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
|
||||
@@ -195,7 +196,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# tablespace_mapping=/path/to/original/tablespace=/path/to/new/tablespace
|
||||
# tablespace_mapping='/path/to/original/tablespace=/path/to/new/tablespace'
|
||||
# restore_command = 'cp /path/to/archived/wals/%f %p'
|
||||
|
||||
#tablespace_mapping='' # Tablespaces can be remapped from one
|
||||
@@ -210,7 +211,9 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# managing WAL archives (see: https://www.pgbarman.org )
|
||||
|
||||
#recovery_min_apply_delay= # If provided, "recovery_min_apply_delay" will be set to
|
||||
# this value (PostgreSQL 9.4 and later).
|
||||
# this value (PostgreSQL 9.4 and later). Value can be
|
||||
# an integer representing milliseconds, or a string
|
||||
# representing a period of time (e.g. '5 min').
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -237,7 +240,8 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# for the new primary to become available
|
||||
#standby_follow_timeout=15 # The max length of time (in seconds) to wait
|
||||
# for the standby to connect to the primary
|
||||
|
||||
#standby_follow_restart=false # Restart the standby instead of sending a SIGHUP
|
||||
# (only for PostgreSQL 13 and later)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# "standby switchover" settings
|
||||
@@ -290,7 +294,6 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# node or follow the new upstream node
|
||||
# 'manual': repmgrd will take no action and the node will require
|
||||
# manual attention to reattach it to replication
|
||||
# (does not apply to BDR mode)
|
||||
|
||||
#priority=100 # indicates a preferred priority for promoting nodes;
|
||||
# a value of zero prevents the node being promoted to primary
|
||||
@@ -299,6 +302,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
#connection_check_type=ping # How to check availability of the upstream node; valid options:
|
||||
# 'ping': use PQping() to check if the node is accepting connections
|
||||
# 'connection': execute a throwaway query on the current connection
|
||||
# 'query': execute an SQL statement on the node via the existing connection
|
||||
#reconnect_attempts=6 # Number of attempts which will be made to reconnect to an unreachable
|
||||
# primary (or other upstream node)
|
||||
#reconnect_interval=10 # Interval between attempts to reconnect to an unreachable
|
||||
@@ -341,6 +345,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# WAL receivers
|
||||
#primary_visibility_consensus=false # If "true", only continue with failover if no standbys have seen
|
||||
# the primary node recently. *Must* be the same on all nodes.
|
||||
#always_promote=false # Always promote a node, even if repmgr metadata is outdated
|
||||
#failover_validation_command='' # Script to execute for an external mechanism to validate the failover
|
||||
# decision made by repmgrd. One or both of the following parameter placeholders
|
||||
# should be provided, which will be replaced by repmgrd with the appropriate
|
||||
@@ -436,12 +441,3 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# issues with shutting down the demotion candidate.
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# BDR monitoring options
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#bdr_local_monitoring_only=false # Only monitor the local node; no checks will be
|
||||
# performed on the other node
|
||||
#bdr_recovery_timeout # If a BDR node was offline and has become available
|
||||
# maximum length of time in seconds to wait for the
|
||||
# node to reconnect to the cluster
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# repmgr extension
|
||||
comment = 'Replication manager for PostgreSQL'
|
||||
default_version = '5.0'
|
||||
default_version = '5.2'
|
||||
module_pathname = '$libdir/repmgr'
|
||||
relocatable = false
|
||||
schema = repmgr
|
||||
|
||||
64
repmgr.h
64
repmgr.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgr.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -74,20 +74,20 @@
|
||||
#include "log.h"
|
||||
#include "sysutils.h"
|
||||
|
||||
#define MIN_SUPPORTED_VERSION "9.3"
|
||||
#define MIN_SUPPORTED_VERSION_NUM 90300
|
||||
#define MIN_SUPPORTED_VERSION "9.4"
|
||||
#define MIN_SUPPORTED_VERSION_NUM 90400
|
||||
|
||||
#define REPLICATION_TYPE_PHYSICAL 1
|
||||
#define REPLICATION_TYPE_BDR 2
|
||||
|
||||
#define UNKNOWN_SERVER_VERSION_NUM -1
|
||||
#define UNKNOWN_BDR_VERSION_NUM -1
|
||||
#define UNKNOWN_REPMGR_VERSION_NUM -1
|
||||
|
||||
#define UNKNOWN_TIMELINE_ID -1
|
||||
#define UNKNOWN_SYSTEM_IDENTIFIER 0
|
||||
#define UNKNOWN_DATA_CHECKSUM_VERSION -1
|
||||
#define UNKNOWN_PID -1
|
||||
#define UNKNOWN_REPLICATION_LAG -1
|
||||
#define UNKNOWN_VALUE -1
|
||||
|
||||
#define NODE_NOT_FOUND -1
|
||||
#define NO_UPSTREAM_NODE -1
|
||||
@@ -98,43 +98,57 @@
|
||||
#define ARCHIVE_STATUS_DIR_ERROR -1
|
||||
#define NO_DEGRADED_MONITORING_ELAPSED -1
|
||||
|
||||
#define BDR2_REPLICATION_SET_NAME "repmgr"
|
||||
#define WALRECEIVER_DISABLE_TIMEOUT_VALUE 86400000 /* milliseconds */
|
||||
|
||||
/*
|
||||
* various default values - ensure repmgr.conf.sample is update
|
||||
* if any of these are changed
|
||||
* Default command line option parameter values
|
||||
*/
|
||||
#define DEFAULT_LOCATION "default"
|
||||
#define DEFAULT_PRIORITY 100
|
||||
#define DEFAULT_RECONNECTION_ATTEMPTS 6 /* seconds */
|
||||
#define DEFAULT_RECONNECTION_INTERVAL 10 /* seconds */
|
||||
#define DEFAULT_MONITORING_INTERVAL 2 /* seconds */
|
||||
#define DEFAULT_ASYNC_QUERY_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_WAIT_START 30 /* seconds */
|
||||
|
||||
/*
|
||||
* Default configuration file parameter values - ensure repmgr.conf.sample
|
||||
* is update if any of these are changed
|
||||
*/
|
||||
|
||||
#define DEFAULT_USE_REPLICATION_SLOTS false
|
||||
#define DEFAULT_USE_PRIMARY_CONNINFO_PASSWORD false
|
||||
#define DEFAULT_PROMOTE_CHECK_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_PROMOTE_CHECK_INTERVAL 1 /* seconds */
|
||||
#define DEFAULT_PRIMARY_FOLLOW_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_STANDBY_FOLLOW_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_BDR_RECOVERY_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_STANDBY_FOLLOW_RESTART false
|
||||
#define DEFAULT_SHUTDOWN_CHECK_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_STANDBY_RECONNECT_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_NODE_REJOIN_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_ARCHIVE_READY_WARNING 16 /* WAL files */
|
||||
#define DEFAULT_ARCHIVE_READY_CRITICAL 128 /* WAL files */
|
||||
#define DEFAULT_REPLICATION_LAG_WARNING 300 /* seconds */
|
||||
#define DEFAULT_REPLICATION_LAG_CRITICAL 600 /* seconds */
|
||||
#define DEFAULT_WITNESS_SYNC_INTERVAL 15 /* seconds */
|
||||
#define DEFAULT_WAIT_START 30 /* seconds */
|
||||
#define DEFAULT_PROMOTE_CHECK_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_PROMOTE_CHECK_INTERVAL 1 /* seconds */
|
||||
#define DEFAULT_SHUTDOWN_CHECK_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_STANDBY_RECONNECT_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_NODE_REJOIN_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_WAL_RECEIVE_CHECK_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_LOCATION "default"
|
||||
#define DEFAULT_PRIORITY 100
|
||||
#define DEFAULT_MONITORING_INTERVAL 2 /* seconds */
|
||||
#define DEFAULT_RECONNECTION_ATTEMPTS 6 /* seconds */
|
||||
#define DEFAULT_RECONNECTION_INTERVAL 10 /* seconds */
|
||||
#define DEFAULT_MONITORING_HISTORY false
|
||||
#define DEFAULT_DEGRADED_MONITORING_TIMEOUT -1 /* seconds */
|
||||
#define DEFAULT_ASYNC_QUERY_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_REPMGRD_STANDBY_STARTUP_TIMEOUT -1 /*seconds */
|
||||
#define DEFAULT_STANDBY_DISCONNECT_ON_FAILOVER false
|
||||
#define DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_CONNECTION_CHECK_TYPE CHECK_PING
|
||||
#define DEFAULT_PRIMARY_VISIBILITY_CONSENSUS false
|
||||
#define DEFAULT_ALWAYS_PROMOTE false
|
||||
#define DEFAULT_ELECTION_RERUN_INTERVAL 15 /* seconds */
|
||||
#define DEFAULT_CHILD_NODES_CHECK_INTERVAL 5 /* seconds */
|
||||
#define DEFAULT_CHILD_NODES_DISCONNECT_MIN_COUNT -1
|
||||
#define DEFAULT_CHILD_NODES_CONNECTED_MIN_COUNT -1
|
||||
#define DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_CHILD_NODES_CONNECTED_INCLUDE_WITNESS false
|
||||
#define DEFAULT_CHILD_NODES_DISCONNECT_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_SSH_OPTIONS "-q -o ConnectTimeout=10"
|
||||
|
||||
#define WALRECEIVER_DISABLE_TIMEOUT_VALUE 86400000 /* milliseconds */
|
||||
|
||||
#ifndef RECOVERY_COMMAND_FILE
|
||||
#define RECOVERY_COMMAND_FILE "recovery.conf"
|
||||
@@ -149,6 +163,6 @@
|
||||
#define TABLESPACE_MAP "tablespace_map"
|
||||
#endif
|
||||
|
||||
|
||||
#define REPMGR_URL "https://repmgr.org/"
|
||||
|
||||
#endif /* _REPMGR_H_ */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define REPMGR_VERSION_DATE ""
|
||||
#define REPMGR_VERSION "5.0.0"
|
||||
#define REPMGR_VERSION_NUM 50000
|
||||
#define REPMGR_RELEASE_DATE "2019-10-15"
|
||||
#define REPMGR_VERSION "5.2.0"
|
||||
#define REPMGR_VERSION_NUM 50200
|
||||
#define REPMGR_RELEASE_DATE "2020-10-22"
|
||||
#define PG_ACTUAL_VERSION_NUM
|
||||
|
||||
678
repmgrd-bdr.c
678
repmgrd-bdr.c
@@ -1,678 +0,0 @@
|
||||
/*
|
||||
* repmgrd-bdr.c - BDR functionality for repmgrd
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
*
|
||||
* 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 <signal.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "repmgrd.h"
|
||||
#include "repmgrd-bdr.h"
|
||||
#include "configfile.h"
|
||||
|
||||
|
||||
static void do_bdr_failover(NodeInfoList *nodes, t_node_info *monitored_node);
|
||||
static void do_bdr_recovery(NodeInfoList *nodes, t_node_info *monitored_node);
|
||||
|
||||
|
||||
void
|
||||
do_bdr_node_check(void)
|
||||
{
|
||||
/* nothing to do at the moment */
|
||||
}
|
||||
|
||||
void
|
||||
handle_sigint_bdr(SIGNAL_ARGS)
|
||||
{
|
||||
PQExpBufferData event_details;
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
_("%s signal received"),
|
||||
postgres_signal_arg == SIGTERM
|
||||
? "TERM" : "INT");
|
||||
|
||||
log_notice("%s", event_details.data);
|
||||
|
||||
create_event_notification(local_conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_shutdown",
|
||||
true,
|
||||
event_details.data);
|
||||
termPQExpBuffer(&event_details);
|
||||
|
||||
terminate(SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
monitor_bdr(void)
|
||||
{
|
||||
NodeInfoList nodes = T_NODE_INFO_LIST_INITIALIZER;
|
||||
t_bdr_node_info bdr_node_info = T_BDR_NODE_INFO_INITIALIZER;
|
||||
RecordStatus record_status;
|
||||
NodeInfoListCell *cell;
|
||||
instr_time log_status_interval_start;
|
||||
|
||||
/* sanity check local database */
|
||||
log_info(_("connecting to local database \"%s\""),
|
||||
config_file_options.conninfo);
|
||||
|
||||
local_conn = establish_db_connection(config_file_options.conninfo, true);
|
||||
|
||||
/*
|
||||
* Local node must be running
|
||||
*/
|
||||
if (PQstatus(local_conn) != CONNECTION_OK)
|
||||
{
|
||||
log_error(_("unable connect to local node (ID: %i), terminating"),
|
||||
local_node_info.node_id);
|
||||
log_hint(_("local node must be running before repmgrd can start"));
|
||||
PQfinish(local_conn);
|
||||
exit(ERR_DB_CONN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that database is a BDR one TODO: check if supported BDR version?
|
||||
*/
|
||||
log_info(_("connected to database, checking for BDR"));
|
||||
|
||||
if (!is_bdr_db(local_conn, NULL))
|
||||
{
|
||||
log_error(_("database is not BDR-enabled"));
|
||||
PQfinish(local_conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check this is a supported BDR version (basically BDR 2.x)
|
||||
*/
|
||||
if (get_bdr_version_num() > 2)
|
||||
{
|
||||
log_error(_("\"bdr\" mode is for BDR 2.x only"));
|
||||
log_hint(_("for BDR 3 and later, use \"replication_type=physical\""));
|
||||
log_error(_("database is not BDR-enabled"));
|
||||
exit(ERR_DB_CONN);
|
||||
}
|
||||
|
||||
if (is_table_in_bdr_replication_set(local_conn, "nodes", "repmgr") == false)
|
||||
{
|
||||
log_error(_("repmgr metadata table 'repmgr.%s' is not in the 'repmgr' replication set"),
|
||||
"nodes");
|
||||
|
||||
/*
|
||||
* TODO: add `repmgr bdr sync` or similar for this situation, and hint
|
||||
* here
|
||||
*/
|
||||
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
record_status = get_bdr_node_record_by_name(local_conn, local_node_info.node_name, &bdr_node_info);
|
||||
|
||||
if (record_status != RECORD_FOUND)
|
||||
{
|
||||
log_error(_("unable to retrieve BDR record for node \"%s\", terminating"),
|
||||
local_node_info.node_name);
|
||||
PQfinish(local_conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (local_node_info.active == false)
|
||||
{
|
||||
log_error(_("local node (ID: %i) is marked as inactive in repmgr"),
|
||||
local_node_info.node_id);
|
||||
log_hint(_("if the node has been reactivated, run \"repmgr bdr register --force\" and restart repmgrd"));
|
||||
PQfinish(local_conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
if (is_active_bdr_node(local_conn, local_node_info.node_name) == false)
|
||||
{
|
||||
log_error(_("BDR node \"%s\" is not active, terminating"),
|
||||
local_node_info.node_name);
|
||||
PQfinish(local_conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/* Log startup event */
|
||||
create_event_record(local_conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_start",
|
||||
true,
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* retrieve list of all nodes - we'll need these if the DB connection goes
|
||||
* away
|
||||
*/
|
||||
if (get_all_node_records(local_conn, &nodes) == false)
|
||||
{
|
||||
/* get_all_node_records() will display the error */
|
||||
PQfinish(local_conn);
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
|
||||
/* we're expecting all (both) nodes to be up */
|
||||
for (cell = nodes.head; cell; cell = cell->next)
|
||||
{
|
||||
cell->node_info->node_status = NODE_STATUS_UP;
|
||||
}
|
||||
|
||||
log_info(_("starting continuous BDR node monitoring on node %i"),
|
||||
config_file_options.node_id);
|
||||
|
||||
INSTR_TIME_SET_CURRENT(log_status_interval_start);
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
/* monitoring loop */
|
||||
log_verbose(LOG_DEBUG, "BDR check loop - checking %i nodes", nodes.node_count);
|
||||
|
||||
for (cell = nodes.head; cell; cell = cell->next)
|
||||
{
|
||||
if (config_file_options.bdr_local_monitoring_only == true
|
||||
&& cell->node_info->node_id != local_node_info.node_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->node_info->node_id == local_node_info.node_id)
|
||||
{
|
||||
log_debug("checking local node %i in %s state",
|
||||
local_node_info.node_id,
|
||||
print_monitoring_state(cell->node_info->monitoring_state));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debug("checking other node %i in %s state",
|
||||
cell->node_info->node_id,
|
||||
print_monitoring_state(cell->node_info->monitoring_state));
|
||||
}
|
||||
|
||||
|
||||
switch (cell->node_info->monitoring_state)
|
||||
{
|
||||
case MS_NORMAL:
|
||||
{
|
||||
if (is_server_available(cell->node_info->conninfo) == false)
|
||||
{
|
||||
/* node is down, we were expecting it to be up */
|
||||
if (cell->node_info->node_status == NODE_STATUS_UP)
|
||||
{
|
||||
instr_time node_unreachable_start;
|
||||
|
||||
INSTR_TIME_SET_CURRENT(node_unreachable_start);
|
||||
|
||||
cell->node_info->node_status = NODE_STATUS_DOWN;
|
||||
|
||||
if (cell->node_info->conn != NULL)
|
||||
{
|
||||
PQfinish(cell->node_info->conn);
|
||||
cell->node_info->conn = NULL;
|
||||
}
|
||||
|
||||
log_warning(_("unable to connect to node \"%s\" (ID %i)"),
|
||||
cell->node_info->node_name, cell->node_info->node_id);
|
||||
try_reconnect(&cell->node_info->conn, cell->node_info);
|
||||
|
||||
/* node has recovered - log and continue */
|
||||
if (cell->node_info->node_status == NODE_STATUS_UP)
|
||||
{
|
||||
int node_unreachable_elapsed = calculate_elapsed(node_unreachable_start);
|
||||
PQExpBufferData event_details;
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
_("reconnected to node %i after %i seconds"),
|
||||
cell->node_info->node_id,
|
||||
node_unreachable_elapsed);
|
||||
log_notice("%s", event_details.data);
|
||||
|
||||
create_event_notification(cell->node_info->conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"bdr_reconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
termPQExpBuffer(&event_details);
|
||||
|
||||
goto loop;
|
||||
}
|
||||
|
||||
/* still down after reconnect attempt(s) */
|
||||
if (cell->node_info->node_status == NODE_STATUS_DOWN)
|
||||
{
|
||||
do_bdr_failover(&nodes, cell->node_info);
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MS_DEGRADED:
|
||||
{
|
||||
/* degraded monitoring */
|
||||
if (is_server_available(cell->node_info->conninfo) == true)
|
||||
{
|
||||
do_bdr_recovery(&nodes, cell->node_info);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop:
|
||||
|
||||
/* emit "still alive" log message at regular intervals, if requested */
|
||||
if (config_file_options.log_status_interval > 0)
|
||||
{
|
||||
int log_status_interval_elapsed = calculate_elapsed(log_status_interval_start);
|
||||
if (log_status_interval_elapsed >= config_file_options.log_status_interval)
|
||||
{
|
||||
log_info(_("monitoring BDR replication status on node \"%s\" (ID: %i)"),
|
||||
local_node_info.node_name,
|
||||
local_node_info.node_id);
|
||||
|
||||
for (cell = nodes.head; cell; cell = cell->next)
|
||||
{
|
||||
if (cell->node_info->monitoring_state == MS_DEGRADED)
|
||||
{
|
||||
log_detail(_("monitoring node \"%s\" (ID: %i) in degraded mode"),
|
||||
cell->node_info->node_name,
|
||||
cell->node_info->node_id);
|
||||
}
|
||||
}
|
||||
INSTR_TIME_SET_CURRENT(log_status_interval_start);
|
||||
}
|
||||
}
|
||||
|
||||
if (got_SIGHUP)
|
||||
{
|
||||
/*
|
||||
* if we can reload, then could need to change local_conn
|
||||
*/
|
||||
if (reload_config(&config_file_options, BDR))
|
||||
{
|
||||
PQfinish(local_conn);
|
||||
local_conn = establish_db_connection(config_file_options.conninfo, true);
|
||||
update_registration(local_conn);
|
||||
}
|
||||
|
||||
got_SIGHUP = false;
|
||||
}
|
||||
|
||||
/* XXX this looks like it will never be called */
|
||||
if (got_SIGHUP)
|
||||
{
|
||||
log_debug("SIGHUP received");
|
||||
|
||||
if (reload_config(&config_file_options, BDR))
|
||||
{
|
||||
PQfinish(local_conn);
|
||||
local_conn = establish_db_connection(config_file_options.conninfo, true);
|
||||
|
||||
if (*config_file_options.log_file)
|
||||
{
|
||||
FILE *fd;
|
||||
|
||||
fd = freopen(config_file_options.log_file, "a", stderr);
|
||||
if (fd == NULL)
|
||||
{
|
||||
fprintf(stderr, "error reopening stderr to \"%s\": %s",
|
||||
config_file_options.log_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
got_SIGHUP = false;
|
||||
}
|
||||
|
||||
log_verbose(LOG_DEBUG, "sleeping %i seconds (\"monitor_interval_secs\")",
|
||||
config_file_options.monitor_interval_secs);
|
||||
sleep(config_file_options.monitor_interval_secs);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_bdr_failover()
|
||||
*
|
||||
* Here we attempt to perform a BDR "failover".
|
||||
*
|
||||
* As there's no equivalent of a physical replication failover,
|
||||
* we'll do the following:
|
||||
*
|
||||
* - connect to active node
|
||||
* - generate an event log record on that node
|
||||
* - optionally execute `bdr_failover_command`, passing the conninfo string
|
||||
* of that node to the command; this can be used for e.g. reconfiguring
|
||||
* pgbouncer.
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
do_bdr_failover(NodeInfoList *nodes, t_node_info *monitored_node)
|
||||
{
|
||||
PGconn *next_node_conn = NULL;
|
||||
NodeInfoListCell *cell;
|
||||
t_event_info event_info = T_EVENT_INFO_INITIALIZER;
|
||||
t_node_info target_node = T_NODE_INFO_INITIALIZER;
|
||||
t_node_info failed_node = T_NODE_INFO_INITIALIZER;
|
||||
RecordStatus record_status;
|
||||
|
||||
/* if one of the two nodes is down, cluster will be in a degraded state */
|
||||
monitored_node->monitoring_state = MS_DEGRADED;
|
||||
INSTR_TIME_SET_CURRENT(degraded_monitoring_start);
|
||||
|
||||
/* terminate local connection if this is the failed node */
|
||||
if (monitored_node->node_id == local_node_info.node_id)
|
||||
{
|
||||
PQfinish(local_conn);
|
||||
local_conn = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* get other node */
|
||||
|
||||
for (cell = nodes->head; cell; cell = cell->next)
|
||||
{
|
||||
log_debug("do_bdr_failover() %s", cell->node_info->node_name);
|
||||
|
||||
/*
|
||||
* don't attempt to connect to the current monitored node, as that's
|
||||
* the one which has failed
|
||||
*/
|
||||
if (cell->node_info->node_id == monitored_node->node_id)
|
||||
continue;
|
||||
|
||||
/* TODO: reuse local conn if local node is up */
|
||||
next_node_conn = establish_db_connection(cell->node_info->conninfo, false);
|
||||
|
||||
if (PQstatus(next_node_conn) == CONNECTION_OK)
|
||||
{
|
||||
record_status = get_node_record(next_node_conn,
|
||||
cell->node_info->node_id,
|
||||
&target_node);
|
||||
|
||||
if (record_status == RECORD_FOUND)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
next_node_conn = NULL;
|
||||
}
|
||||
|
||||
/* shouldn't happen, and if it does, it means everything is down */
|
||||
if (next_node_conn == NULL)
|
||||
{
|
||||
log_error(_("no other available node found"));
|
||||
|
||||
/* no other nodes found - continue degraded monitoring */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if the node record for the failed node is still marked as active,
|
||||
* if not it means the other node has done the "failover" already
|
||||
*/
|
||||
|
||||
record_status = get_node_record(next_node_conn,
|
||||
monitored_node->node_id,
|
||||
&failed_node);
|
||||
|
||||
if (record_status == RECORD_FOUND && failed_node.active == false)
|
||||
{
|
||||
PQfinish(next_node_conn);
|
||||
log_notice(_("record for node %i has already been set inactive"),
|
||||
failed_node.node_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (am_bdr_failover_handler(next_node_conn, local_node_info.node_id) == false)
|
||||
{
|
||||
PQfinish(next_node_conn);
|
||||
log_notice(_("other node's repmgrd is handling failover"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* check here that the node hasn't come back up */
|
||||
if (is_server_available(monitored_node->conninfo) == true)
|
||||
{
|
||||
log_notice(_("node %i has reappeared, aborting failover"),
|
||||
monitored_node->node_id);
|
||||
monitored_node->monitoring_state = MS_NORMAL;
|
||||
PQfinish(next_node_conn);
|
||||
}
|
||||
|
||||
log_debug("this node is the failover handler");
|
||||
|
||||
{
|
||||
PQExpBufferData event_details;
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
event_info.conninfo_str = target_node.conninfo;
|
||||
event_info.node_name = target_node.node_name;
|
||||
|
||||
/* update node record on the active node */
|
||||
update_node_record_set_active(next_node_conn, monitored_node->node_id, false);
|
||||
|
||||
log_notice(_("setting node record for node %i to inactive"), monitored_node->node_id);
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
_("node \"%s\" (ID: %i) detected as failed; next available node is \"%s\" (ID: %i)"),
|
||||
monitored_node->node_name,
|
||||
monitored_node->node_id,
|
||||
target_node.node_name,
|
||||
target_node.node_id);
|
||||
|
||||
/*
|
||||
* Create an event record
|
||||
*
|
||||
* If we were able to connect to another node, we'll update the event log
|
||||
* there.
|
||||
*
|
||||
* In any case the event notification command will be triggered with the
|
||||
* event "bdr_failover"
|
||||
*/
|
||||
|
||||
|
||||
create_event_notification_extended(next_node_conn,
|
||||
&config_file_options,
|
||||
monitored_node->node_id,
|
||||
"bdr_failover",
|
||||
true,
|
||||
event_details.data,
|
||||
&event_info);
|
||||
|
||||
log_info("%s", event_details.data);
|
||||
|
||||
termPQExpBuffer(&event_details);
|
||||
}
|
||||
|
||||
unset_bdr_failover_handler(next_node_conn);
|
||||
|
||||
PQfinish(next_node_conn);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
do_bdr_recovery(NodeInfoList *nodes, t_node_info *monitored_node)
|
||||
{
|
||||
PGconn *recovered_node_conn;
|
||||
|
||||
t_event_info event_info = T_EVENT_INFO_INITIALIZER;
|
||||
int i;
|
||||
bool slot_reactivated = false;
|
||||
int node_recovery_elapsed;
|
||||
|
||||
char node_name[MAXLEN] = "";
|
||||
|
||||
log_debug("handling recovery for monitored node %i", monitored_node->node_id);
|
||||
|
||||
recovered_node_conn = establish_db_connection(monitored_node->conninfo, false);
|
||||
|
||||
if (PQstatus(recovered_node_conn) != CONNECTION_OK)
|
||||
{
|
||||
PQfinish(recovered_node_conn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PQstatus(local_conn) != CONNECTION_OK)
|
||||
{
|
||||
log_debug("no local connection - attempting to reconnect ");
|
||||
local_conn = establish_db_connection(config_file_options.conninfo, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* still unable to connect - the local node is probably down, so we can't
|
||||
* check for reconnection
|
||||
*/
|
||||
if (PQstatus(local_conn) != CONNECTION_OK)
|
||||
{
|
||||
PQExpBufferData event_details;
|
||||
|
||||
local_conn = NULL;
|
||||
log_warning(_("unable to reconnect to local node"));
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
node_recovery_elapsed = calculate_elapsed(degraded_monitoring_start);
|
||||
monitored_node->monitoring_state = MS_NORMAL;
|
||||
monitored_node->node_status = NODE_STATUS_UP;
|
||||
|
||||
appendPQExpBuffer(
|
||||
&event_details,
|
||||
_("node \"%s\" (ID: %i) has become available after %i seconds"),
|
||||
monitored_node->node_name,
|
||||
monitored_node->node_id,
|
||||
node_recovery_elapsed);
|
||||
|
||||
log_notice("%s", event_details.data);
|
||||
|
||||
termPQExpBuffer(&event_details);
|
||||
|
||||
PQfinish(recovered_node_conn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
get_bdr_other_node_name(local_conn, local_node_info.node_id, node_name);
|
||||
|
||||
log_info(_("detected recovery on node \"%s\" (ID: %i), checking status"),
|
||||
monitored_node->node_name,
|
||||
monitored_node->node_id);
|
||||
|
||||
for (i = 0; i < config_file_options.bdr_recovery_timeout; i++)
|
||||
{
|
||||
ReplSlotStatus slot_status;
|
||||
|
||||
log_debug("checking for state of replication slot for node \"%s\"", node_name);
|
||||
|
||||
slot_status = get_bdr_node_replication_slot_status(
|
||||
local_conn,
|
||||
node_name);
|
||||
|
||||
if (slot_status == SLOT_ACTIVE)
|
||||
{
|
||||
slot_reactivated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
/* mark node as up */
|
||||
monitored_node->node_status = NODE_STATUS_UP;
|
||||
|
||||
if (slot_reactivated == false)
|
||||
{
|
||||
log_warning(_("no active replication slot for node \"%s\" found after %i seconds"),
|
||||
node_name,
|
||||
config_file_options.bdr_recovery_timeout);
|
||||
log_detail(_("this probably means inter-node BDR connections have not been re-established"));
|
||||
PQfinish(recovered_node_conn);
|
||||
return;
|
||||
}
|
||||
|
||||
log_info(_("active replication slot for node \"%s\" found after %i seconds"),
|
||||
node_name,
|
||||
i);
|
||||
|
||||
node_recovery_elapsed = calculate_elapsed(degraded_monitoring_start);
|
||||
monitored_node->monitoring_state = MS_NORMAL;
|
||||
|
||||
{
|
||||
PQExpBufferData event_details;
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
_("node \"%s\" (ID: %i) has recovered after %i seconds"),
|
||||
monitored_node->node_name,
|
||||
monitored_node->node_id,
|
||||
node_recovery_elapsed);
|
||||
|
||||
log_notice("%s", event_details.data);
|
||||
|
||||
|
||||
/* other node will generate the event */
|
||||
if (monitored_node->node_id == local_node_info.node_id)
|
||||
{
|
||||
termPQExpBuffer(&event_details);
|
||||
PQfinish(recovered_node_conn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* generate the event on the currently active node only */
|
||||
if (monitored_node->node_id != local_node_info.node_id)
|
||||
{
|
||||
event_info.conninfo_str = monitored_node->conninfo;
|
||||
event_info.node_name = monitored_node->node_name;
|
||||
|
||||
create_event_notification_extended(local_conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"bdr_recovery",
|
||||
true,
|
||||
event_details.data,
|
||||
&event_info);
|
||||
}
|
||||
|
||||
termPQExpBuffer(&event_details);
|
||||
}
|
||||
|
||||
update_node_record_set_active(local_conn, monitored_node->node_id, true);
|
||||
|
||||
PQfinish(recovered_node_conn);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* repmgrd-bdr.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
*
|
||||
* 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 _REPMGRD_BDR_H_
|
||||
#define _REPMGRD_BDR_H_
|
||||
|
||||
extern void do_bdr_node_check(void);
|
||||
extern void monitor_bdr(void);
|
||||
|
||||
extern void handle_sigint_bdr(SIGNAL_ARGS);
|
||||
#endif /* _REPMGRD_BDR_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgrd-physical.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
|
||||
122
repmgrd.c
122
repmgrd.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* repmgrd.c - Replication manager daemon
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "repmgr.h"
|
||||
#include "repmgrd.h"
|
||||
#include "repmgrd-physical.h"
|
||||
#include "repmgrd-bdr.h"
|
||||
#include "configfile.h"
|
||||
#include "voting.h"
|
||||
|
||||
@@ -40,13 +39,9 @@ static bool daemonize = true;
|
||||
static bool show_pid_file = false;
|
||||
static bool no_pid_file = false;
|
||||
|
||||
t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER;
|
||||
|
||||
t_node_info local_node_info = T_NODE_INFO_INITIALIZER;
|
||||
PGconn *local_conn = NULL;
|
||||
|
||||
|
||||
|
||||
/* Collate command line errors here for friendlier reporting */
|
||||
static ItemList cli_errors = {NULL, NULL};
|
||||
|
||||
@@ -257,7 +252,7 @@ main(int argc, char **argv)
|
||||
* locations). If no conifguration file is available, or it can't be parsed
|
||||
* parse_config() will abort anyway, with an appropriate message.
|
||||
*/
|
||||
load_config(config_file, verbose, false, &config_file_options, argv[0]);
|
||||
load_config(config_file, verbose, false, argv[0]);
|
||||
|
||||
/* Determine pid file location, unless --no-pid-file supplied */
|
||||
|
||||
@@ -484,9 +479,6 @@ main(int argc, char **argv)
|
||||
case REPLICATION_TYPE_PHYSICAL:
|
||||
log_hint(_("check that 'repmgr (primary|standby) register' was executed for this node"));
|
||||
break;
|
||||
case REPLICATION_TYPE_BDR:
|
||||
log_hint(_("check that 'repmgr bdr register' was executed for this node"));
|
||||
break;
|
||||
}
|
||||
|
||||
close_connection(&local_conn);
|
||||
@@ -513,12 +505,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (config_file_options.replication_type == REPLICATION_TYPE_BDR)
|
||||
{
|
||||
log_debug("node id is %i", local_node_info.node_id);
|
||||
do_bdr_node_check();
|
||||
}
|
||||
else
|
||||
if (config_file_options.replication_type == REPLICATION_TYPE_PHYSICAL)
|
||||
{
|
||||
log_debug("node id is %i, upstream node id is %i",
|
||||
local_node_info.node_id,
|
||||
@@ -526,8 +513,6 @@ main(int argc, char **argv)
|
||||
do_physical_node_check();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (daemonize == true)
|
||||
{
|
||||
daemonize_process();
|
||||
@@ -576,9 +561,6 @@ start_monitoring(void)
|
||||
case WITNESS:
|
||||
monitor_streaming_witness();
|
||||
break;
|
||||
case BDR:
|
||||
monitor_bdr();
|
||||
return;
|
||||
case UNKNOWN:
|
||||
/* should never happen */
|
||||
break;
|
||||
@@ -771,10 +753,6 @@ setup_event_handlers(void)
|
||||
*/
|
||||
switch (config_file_options.replication_type)
|
||||
{
|
||||
case REPLICATION_TYPE_BDR:
|
||||
pqsignal(SIGINT, handle_sigint_bdr);
|
||||
pqsignal(SIGTERM, handle_sigint_bdr);
|
||||
break;
|
||||
case REPLICATION_TYPE_PHYSICAL:
|
||||
pqsignal(SIGINT, handle_sigint_physical);
|
||||
pqsignal(SIGTERM, handle_sigint_physical);
|
||||
@@ -830,42 +808,92 @@ show_help(void)
|
||||
|
||||
|
||||
bool
|
||||
check_upstream_connection(PGconn **conn, const char *conninfo)
|
||||
check_upstream_connection(PGconn **conn, const char *conninfo, PGconn **paired_conn)
|
||||
{
|
||||
/* Check the connection status twice in case it changes after reset */
|
||||
bool twice = false;
|
||||
|
||||
if (config_file_options.connection_check_type == CHECK_PING)
|
||||
return is_server_available(conninfo);
|
||||
|
||||
if (config_file_options.connection_check_type == CHECK_CONNECTION)
|
||||
log_debug("connection check type is \"%s\"",
|
||||
print_connection_check_type(config_file_options.connection_check_type));
|
||||
/*
|
||||
* For the check types which do not involve using the existing database
|
||||
* connection, we'll perform the actual check, then as an additional
|
||||
* safeguard verify that the connection is still valid (as it might have
|
||||
* gone away during a brief outage between checks).
|
||||
*/
|
||||
if (config_file_options.connection_check_type != CHECK_QUERY)
|
||||
{
|
||||
bool success = true;
|
||||
PGconn *test_conn = PQconnectdb(conninfo);
|
||||
|
||||
log_debug("check_upstream_connection(): attempting to connect to \"%s\"", conninfo);
|
||||
|
||||
if (PQstatus(test_conn) != CONNECTION_OK)
|
||||
if (config_file_options.connection_check_type == CHECK_PING)
|
||||
{
|
||||
log_warning(_("unable to connect to \"%s\""), conninfo);
|
||||
log_detail("\n%s", PQerrorMessage(test_conn));
|
||||
success = false;
|
||||
success = is_server_available(conninfo);
|
||||
}
|
||||
PQfinish(test_conn);
|
||||
else if (config_file_options.connection_check_type == CHECK_CONNECTION)
|
||||
{
|
||||
/*
|
||||
* This connection is thrown away, and we never execute a query on it.
|
||||
*/
|
||||
PGconn *test_conn = PQconnectdb(conninfo);
|
||||
|
||||
return success;
|
||||
log_debug("check_upstream_connection(): attempting to connect to \"%s\"", conninfo);
|
||||
|
||||
if (PQstatus(test_conn) != CONNECTION_OK)
|
||||
{
|
||||
log_warning(_("unable to connect to \"%s\""), conninfo);
|
||||
log_detail("\n%s", PQerrorMessage(test_conn));
|
||||
success = false;
|
||||
}
|
||||
PQfinish(test_conn);
|
||||
}
|
||||
|
||||
if (success == false)
|
||||
return false;
|
||||
|
||||
if (PQstatus(*conn) == CONNECTION_OK)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Checks have succeeded, but the open connection to the primary has gone away,
|
||||
* possibly due to a brief outage between monitoring intervals - attempt to
|
||||
* reset it.
|
||||
*/
|
||||
log_notice(_("upstream is available but upstream connection has gone away, resetting"));
|
||||
|
||||
PQfinish(*conn);
|
||||
*conn = establish_db_connection_quiet(conninfo);
|
||||
|
||||
if (PQstatus(*conn) == CONNECTION_OK)
|
||||
{
|
||||
if (paired_conn != NULL)
|
||||
{
|
||||
log_debug("resetting paired connection");
|
||||
*paired_conn = *conn;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (PQstatus(*conn) != CONNECTION_OK)
|
||||
{
|
||||
log_debug("check_upstream_connection(): connection not OK");
|
||||
log_debug("check_upstream_connection(): upstream connection has gone away, resetting");
|
||||
if (twice)
|
||||
return false;
|
||||
|
||||
/* reconnect */
|
||||
PQfinish(*conn);
|
||||
*conn = PQconnectdb(conninfo);
|
||||
*conn = establish_db_connection_quiet(conninfo);
|
||||
|
||||
if (paired_conn != NULL)
|
||||
{
|
||||
log_debug("resetting paired connection");
|
||||
*paired_conn = *conn;
|
||||
}
|
||||
twice = true;
|
||||
}
|
||||
else
|
||||
@@ -877,7 +905,7 @@ check_upstream_connection(PGconn **conn, const char *conninfo)
|
||||
goto failed;
|
||||
|
||||
/* execute a simple query to verify connection availability */
|
||||
if (PQsendQuery(*conn, "SELECT 1") == 0)
|
||||
if (PQsendQuery(*conn, config_file_options.connection_check_query) == 0)
|
||||
{
|
||||
log_warning(_("unable to send query to upstream"));
|
||||
log_detail("%s", PQerrorMessage(*conn));
|
||||
@@ -895,8 +923,16 @@ check_upstream_connection(PGconn **conn, const char *conninfo)
|
||||
return false;
|
||||
|
||||
/* reconnect */
|
||||
log_debug("check_upstream_connection(): upstream connection not available, resetting");
|
||||
|
||||
PQfinish(*conn);
|
||||
*conn = PQconnectdb(conninfo);
|
||||
*conn = establish_db_connection_quiet(conninfo);
|
||||
|
||||
if (paired_conn != NULL)
|
||||
{
|
||||
log_debug("resetting paired connection");
|
||||
*paired_conn = *conn;
|
||||
}
|
||||
twice = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* repmgrd.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*/
|
||||
|
||||
|
||||
@@ -17,13 +17,12 @@ extern volatile sig_atomic_t got_SIGHUP;
|
||||
extern MonitoringState monitoring_state;
|
||||
extern instr_time degraded_monitoring_start;
|
||||
|
||||
extern t_configuration_options config_file_options;
|
||||
extern t_node_info local_node_info;
|
||||
extern PGconn *local_conn;
|
||||
extern bool startup_event_logged;
|
||||
extern char pid_file[MAXPGPATH];
|
||||
|
||||
bool check_upstream_connection(PGconn **conn, const char *conninfo);
|
||||
bool check_upstream_connection(PGconn **conn, const char *conninfo, PGconn **paired_conn);
|
||||
void try_reconnect(PGconn **conn, t_node_info *node_info);
|
||||
|
||||
int calculate_elapsed(instr_time start_time);
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u
|
||||
set -e
|
||||
|
||||
# Process parameters passed to script
|
||||
# -----------------------------------
|
||||
#
|
||||
# This assumes the repmgr "event_notification_command" is defined like this:
|
||||
#
|
||||
# event_notification_command='/path/to/bdr-pgbouncer.sh %n %e %s "%c" "%a" >> /tmp/bdr-failover.log 2>&1'
|
||||
#
|
||||
# Adjust as appropriate.
|
||||
|
||||
NODE_ID=$1
|
||||
EVENT_TYPE=$2
|
||||
SUCCESS=$3
|
||||
NEXT_CONNINFO=$4
|
||||
NEXT_NODE_NAME=$5
|
||||
|
||||
if [ "$EVENT_TYPE" != "bdr_failover" ]; then
|
||||
echo "unable to handle event type '$EVENT_TYPE'"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Define database name here
|
||||
# -------------------------
|
||||
#
|
||||
# Note: this assumes the BDR-enabled database has the same name on
|
||||
# both hosts
|
||||
|
||||
BDR_DBNAME=bdr_db
|
||||
|
||||
# Define PgBouncer hosts here
|
||||
# ---------------------------
|
||||
|
||||
PGBOUNCER_HOSTS="host1 host2"
|
||||
PGBOUNCER_PORTS=(6432 6432)
|
||||
PGBOUNCER_DATABASE_INI=(/path/to/pgbouncer.database.ini /path/to/pgbouncer.database.ini)
|
||||
|
||||
|
||||
# Define local host info here
|
||||
# ---------------------------
|
||||
|
||||
THIS_HOST="host1"
|
||||
THIS_PGBOUNCER_PORT="6432"
|
||||
THIS_DB_PORT="5432"
|
||||
|
||||
# Pause all pgbouncer nodes to minimize impact on clients
|
||||
# -------------------------------------------------------
|
||||
|
||||
i=0
|
||||
for HOST in $PGBOUNCER_HOSTS
|
||||
do
|
||||
PORT="${PGBOUNCER_PORTS[$i]}"
|
||||
|
||||
psql -tc "pause" -h $HOST -p $PORT -U postgres pgbouncer
|
||||
|
||||
i=$((i+1))
|
||||
done
|
||||
|
||||
# Copy pgbouncer database ini file to all nodes and restart pgbouncer
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
i=0
|
||||
THIS_HOSTPORT="$THIS_HOST$THIS_PGBOUNCER_PORT"
|
||||
PGBOUNCER_DATABASE_INI_NEW="/tmp/pgbouncer.database.ini.new"
|
||||
|
||||
for HOST in $PGBOUNCER_HOSTS
|
||||
do
|
||||
PORT="${PGBOUNCER_PORTS[$i]}"
|
||||
|
||||
# Recreate the pgbouncer config file
|
||||
# ----------------------------------
|
||||
echo -e "[databases]\n" > $PGBOUNCER_DATABASE_INI_NEW
|
||||
|
||||
echo -e "$BDR_DBNAME= $NEXT_CONNINFO application_name=pgbouncer_$PORT" >> $PGBOUNCER_DATABASE_INI_NEW
|
||||
|
||||
# Copy file to host
|
||||
# -----------------
|
||||
CONFIG="${PGBOUNCER_DATABASE_INI[$i]}"
|
||||
|
||||
if [ "$HOST$PORT" != "$THIS_HOSTPORT" ]; then
|
||||
rsync $PGBOUNCER_DATABASE_INI_NEW $HOST:$CONFIG
|
||||
else
|
||||
cp $PGBOUNCER_DATABASE_INI_NEW $CONFIG
|
||||
fi
|
||||
|
||||
# Reload and resume PgBouncer
|
||||
# ---------------------------
|
||||
|
||||
psql -tc "reload" -h $HOST -p $PORT -U postgres pgbouncer
|
||||
psql -tc "resume" -h $HOST -p $PORT -U postgres pgbouncer
|
||||
|
||||
i=$((i+1))
|
||||
done
|
||||
|
||||
|
||||
# Clean up generated file
|
||||
rm $PGBOUNCER_DATABASE_INI_NEW
|
||||
|
||||
echo "Reconfiguration of pgbouncer complete"
|
||||
@@ -17,8 +17,6 @@ SELECT * FROM repmgr.replication_status;
|
||||
SELECT * FROM repmgr.show_nodes;
|
||||
|
||||
-- functions
|
||||
SELECT repmgr.am_bdr_failover_handler(-1);
|
||||
SELECT repmgr.am_bdr_failover_handler(NULL);
|
||||
SELECT repmgr.get_new_primary();
|
||||
SELECT repmgr.notify_follow_primary(-1);
|
||||
SELECT repmgr.notify_follow_primary(NULL);
|
||||
@@ -27,4 +25,3 @@ SELECT repmgr.set_local_node_id(-1);
|
||||
SELECT repmgr.set_local_node_id(NULL);
|
||||
SELECT repmgr.standby_get_last_updated();
|
||||
SELECT repmgr.standby_set_last_updated();
|
||||
SELECT repmgr.unset_bdr_failover_handler();
|
||||
|
||||
13
strutil.c
13
strutil.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* strutil.c
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -386,12 +386,9 @@ output_check_status(CheckStatus status)
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Escape a string for use as a parameter in recovery.conf
|
||||
* Caller must free returned value
|
||||
@@ -433,7 +430,6 @@ escape_string(PGconn *conn, const char *string)
|
||||
/*
|
||||
* simple function to escape double quotes only
|
||||
*/
|
||||
|
||||
void
|
||||
escape_double_quotes(char *string, PQExpBufferData *out)
|
||||
{
|
||||
@@ -564,3 +560,10 @@ parse_follow_command(char *parsed_command, char *template, int node_id)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
format_bool(bool value)
|
||||
{
|
||||
return value == true ? "true" : "false";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* strutil.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -166,4 +166,6 @@ extern char *trim(char *s);
|
||||
extern void
|
||||
parse_follow_command(char *parsed_command, char *template, int node_id);
|
||||
|
||||
extern const char *format_bool(bool value);
|
||||
|
||||
#endif /* _STRUTIL_H_ */
|
||||
|
||||
106
sysutils.c
106
sysutils.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Functions which need to be executed on the local system.
|
||||
*
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -57,27 +57,56 @@ _local_command(const char *command, PQExpBufferData *outputbuf, bool simple, int
|
||||
char output[MAXLEN];
|
||||
int retval = 0;
|
||||
bool success;
|
||||
char tmpfile_path[MAXPGPATH];
|
||||
const char *tmpdir = getenv("TMPDIR");
|
||||
int fd;
|
||||
PQExpBufferData command_final;
|
||||
|
||||
log_verbose(LOG_DEBUG, "executing:\n %s", command);
|
||||
if (!tmpdir)
|
||||
tmpdir = "/tmp";
|
||||
|
||||
maxpath_snprintf(tmpfile_path, "%s/repmgr_command.XXXXXX",
|
||||
tmpdir);
|
||||
|
||||
fd = mkstemp(tmpfile_path);
|
||||
|
||||
if (fd < 1)
|
||||
{
|
||||
log_error(_("unable to open temporary file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
initPQExpBuffer(&command_final);
|
||||
appendPQExpBufferStr(&command_final, command);
|
||||
|
||||
appendPQExpBuffer(&command_final, " 2>%s", tmpfile_path);
|
||||
|
||||
log_verbose(LOG_DEBUG, "executing:\n %s", command_final.data);
|
||||
|
||||
if (outputbuf == NULL)
|
||||
{
|
||||
retval = system(command);
|
||||
retval = system(command_final.data);
|
||||
termPQExpBuffer(&command_final);
|
||||
|
||||
if (return_value != NULL)
|
||||
*return_value = WEXITSTATUS(retval);
|
||||
|
||||
close(fd);
|
||||
|
||||
return (retval == 0) ? true : false;
|
||||
}
|
||||
|
||||
fp = popen(command, "r");
|
||||
fp = popen(command_final.data, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
log_error(_("unable to execute local command:\n%s"), command);
|
||||
log_error(_("unable to execute local command:\n%s"), command_final.data);
|
||||
termPQExpBuffer(&command_final);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
termPQExpBuffer(&command_final);
|
||||
|
||||
while (fgets(output, MAXLEN, fp) != NULL)
|
||||
{
|
||||
@@ -91,11 +120,32 @@ _local_command(const char *command, PQExpBufferData *outputbuf, bool simple, int
|
||||
|
||||
retval = pclose(fp);
|
||||
|
||||
/* */
|
||||
/* 141 = SIGPIPE */
|
||||
success = (WEXITSTATUS(retval) == 0 || WEXITSTATUS(retval) == 141) ? true : false;
|
||||
|
||||
log_verbose(LOG_DEBUG, "result of command was %i (%i)", WEXITSTATUS(retval), retval);
|
||||
|
||||
/*
|
||||
* Append any captured STDERR output
|
||||
*/
|
||||
|
||||
fp = fopen(tmpfile_path, "r");
|
||||
|
||||
/*
|
||||
* Not critical if we can't open the file
|
||||
*/
|
||||
if (fp != NULL)
|
||||
{
|
||||
while (fgets(output, MAXLEN, fp) != NULL)
|
||||
{
|
||||
appendPQExpBufferStr(outputbuf, output);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
unlink(tmpfile_path);
|
||||
|
||||
if (return_value != NULL)
|
||||
*return_value = WEXITSTATUS(retval);
|
||||
|
||||
@@ -104,6 +154,7 @@ _local_command(const char *command, PQExpBufferData *outputbuf, bool simple, int
|
||||
else
|
||||
log_verbose(LOG_DEBUG, "local_command(): no output returned");
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -118,28 +169,13 @@ remote_command(const char *host, const char *user, const char *command, const ch
|
||||
{
|
||||
FILE *fp;
|
||||
PQExpBufferData ssh_command;
|
||||
PQExpBufferData ssh_host;
|
||||
|
||||
char output[MAXLEN] = "";
|
||||
|
||||
initPQExpBuffer(&ssh_host);
|
||||
|
||||
if (*user != '\0')
|
||||
{
|
||||
appendPQExpBuffer(&ssh_host, "%s@", user);
|
||||
}
|
||||
|
||||
appendPQExpBufferStr(&ssh_host, host);
|
||||
|
||||
initPQExpBuffer(&ssh_command);
|
||||
|
||||
appendPQExpBuffer(&ssh_command,
|
||||
"ssh -o Batchmode=yes %s %s %s",
|
||||
ssh_options,
|
||||
ssh_host.data,
|
||||
command);
|
||||
|
||||
termPQExpBuffer(&ssh_host);
|
||||
make_remote_command(host, user, command, ssh_options, &ssh_command);
|
||||
|
||||
log_debug("remote_command():\n %s", ssh_command.data);
|
||||
|
||||
@@ -187,6 +223,32 @@ remote_command(const char *host, const char *user, const char *command, const ch
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
make_remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *ssh_command)
|
||||
{
|
||||
PQExpBufferData ssh_host;
|
||||
|
||||
initPQExpBuffer(&ssh_host);
|
||||
|
||||
if (*user != '\0')
|
||||
{
|
||||
appendPQExpBuffer(&ssh_host, "%s@", user);
|
||||
}
|
||||
|
||||
appendPQExpBufferStr(&ssh_host, host);
|
||||
|
||||
|
||||
appendPQExpBuffer(ssh_command,
|
||||
"ssh -o Batchmode=yes %s %s %s",
|
||||
ssh_options,
|
||||
ssh_host.data,
|
||||
command);
|
||||
|
||||
termPQExpBuffer(&ssh_host);
|
||||
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
disable_wal_receiver(PGconn *conn)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* sysutils.h
|
||||
* Copyright (c) 2ndQuadrant, 2010-2019
|
||||
* Copyright (c) 2ndQuadrant, 2010-2020
|
||||
*
|
||||
* 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
|
||||
@@ -24,6 +24,7 @@ extern bool local_command_return_value(const char *command, PQExpBufferData *out
|
||||
extern bool local_command_simple(const char *command, PQExpBufferData *outputbuf);
|
||||
|
||||
extern bool remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *outputbuf);
|
||||
extern void make_remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *ssh_command);
|
||||
|
||||
extern pid_t disable_wal_receiver(PGconn *conn);
|
||||
extern pid_t enable_wal_receiver(PGconn *conn, bool wait_startup);
|
||||
|
||||
Reference in New Issue
Block a user