mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 15:16:29 +00:00
Compare commits
60 Commits
dev/drop_r
...
dev/HL-38/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99bbf207b5 | ||
|
|
836894c965 | ||
|
|
7f3a26f0d9 | ||
|
|
a469221e28 | ||
|
|
c6d9f38458 | ||
|
|
5abdbb0d39 | ||
|
|
8f27e3bc5d | ||
|
|
b92d43d136 | ||
|
|
4a28c57bc7 | ||
|
|
f69485c0ba | ||
|
|
b4a0938081 | ||
|
|
569f906003 | ||
|
|
94b21ae8ac | ||
|
|
4c9cca64d0 | ||
|
|
82e2fd66e1 | ||
|
|
90fe1b8135 | ||
|
|
1cd168360e | ||
|
|
e8aa3aced7 | ||
|
|
d3b1ff45b0 | ||
|
|
450786ec29 | ||
|
|
70b34308cc | ||
|
|
19c92a7092 | ||
|
|
520ff25ef3 | ||
|
|
8e81c04b4a | ||
|
|
aad988c292 | ||
|
|
43b8a5f65f | ||
|
|
26cfb56170 | ||
|
|
f0cc225de0 | ||
|
|
02d8e0c808 | ||
|
|
167d166ae8 | ||
|
|
03c2ae1bd8 | ||
|
|
4021037d38 | ||
|
|
7cd7566409 | ||
|
|
81c3200ef2 | ||
|
|
c6366db6f9 | ||
|
|
078a47e182 | ||
|
|
ed2c0aaf0b | ||
|
|
6249027a03 | ||
|
|
b36fca17d9 | ||
|
|
a3b919d599 | ||
|
|
4c95d8d75e | ||
|
|
c21d452076 | ||
|
|
3b89731899 | ||
|
|
138bee98e9 | ||
|
|
83ffe84ff5 | ||
|
|
bb3206a2bf | ||
|
|
49dfaea471 | ||
|
|
8edc64f64e | ||
|
|
3ce646f960 | ||
|
|
dc0e89e234 | ||
|
|
41b6194580 | ||
|
|
d501781a5f | ||
|
|
de5265f594 | ||
|
|
66ac4183b4 | ||
|
|
59e5bc1500 | ||
|
|
8164914598 | ||
|
|
eb867516ff | ||
|
|
6b961ab6a7 | ||
|
|
7e2d14d225 | ||
|
|
a90d1cf3dd |
7
.github/CODEOWNERS
vendored
Normal file
7
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo. Unless a later match takes precedence,
|
||||
# @global-owner1 and @global-owner2 will be requested for
|
||||
# review when someone opens a pull request.
|
||||
* @EnterpriseDB/repmgr-dev
|
||||
37
.github/workflows/sonarqube-scan.yml
vendored
37
.github/workflows/sonarqube-scan.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: SonarQube Scan
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
sonarQube:
|
||||
name: SonarQube-Job
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout source repo
|
||||
uses: actions/checkout@v1
|
||||
with:
|
||||
ref: '${{ github.head_ref }}'
|
||||
|
||||
- name: Checkout GitHub Action Repo
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
repository: EnterpriseDB/edb-github-actions.git
|
||||
ref: master
|
||||
token: ${{ secrets.GH_SLONIK }}
|
||||
path: .github/actions/edb-github-actions
|
||||
|
||||
- name: SonarQube Scan
|
||||
uses: ./.github/actions/edb-github-actions/sonarqube
|
||||
with:
|
||||
REPO_NAME: '${{github.event.repository.name}}'
|
||||
SONAR_PROJECT_KEY: EnterpriseDB_repmgr
|
||||
SONAR_URL: '${{secrets.SONARQUBE_URL}}'
|
||||
SONAR_LOGIN: '${{secrets.SONARQUBE_LOGIN}}'
|
||||
PULL_REQUEST_KEY: '${{github.event.number}}'
|
||||
PULL_REQUEST_BRANCH: '${{github.head_ref}}'
|
||||
PULL_REQUEST_BASE_BRANCH: '${{github.base_ref}}'
|
||||
REPO_DEFAULT_BRANCH: '${{github.event.repository.default_branch}}'
|
||||
REPO_EXCLUDE_FILES: '*properties*,**/src/test/**/*,**/*.sql,**/docs/**/*,**/*/*.java'
|
||||
18
.github/workflows/sonarqube/configure-env.sh
vendored
18
.github/workflows/sonarqube/configure-env.sh
vendored
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get -y install debhelper curl autoconf zlib1g-dev \
|
||||
libedit-dev libxml2-dev libxslt1-dev libkrb5-dev libssl-dev libpam0g-dev systemtap-sdt-dev \
|
||||
libselinux1-dev build-essential bison apt-utils lsb-release devscripts \
|
||||
software-properties-common git shellcheck flex
|
||||
|
||||
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
|
||||
sudo apt-get -y install libpq-dev postgresql-13 postgresql-server-dev-13
|
||||
|
||||
./configure
|
||||
|
||||
export PG_CONFIG=/usr/bin/pg_config
|
||||
|
||||
/home/buildfarm/sonar/depends/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir build_wrapper_output_directory make
|
||||
22
HISTORY
22
HISTORY
@@ -1,8 +1,26 @@
|
||||
5.3.2 2022-??-??
|
||||
5.5.0 2024-11-20
|
||||
Support for PostgreSQL 17 added
|
||||
Fix warnings detected by the -Wshadow=compatible-local
|
||||
added in PostgreSQL 16
|
||||
|
||||
5.4.1 2023-07-04
|
||||
repmgrd: ensure witness node metadata is updated (Ian)
|
||||
|
||||
5.4.0 2023-03-16
|
||||
Support cloning replicas using pg-backup-api
|
||||
|
||||
5.3.3 2022-10-17
|
||||
Support for PostgreSQL added
|
||||
repmgrd: ensure event notification script is called for event
|
||||
"repmgrd_upstream_disconnect"; GitHub #760 (Ian)
|
||||
|
||||
5.3.2 2022-05-25
|
||||
standby clone: don't error out if unable to determine cluster size (Ian)
|
||||
node check: fix --downstream --nagios output; GitHub #749 (Ian)
|
||||
repmgrd: ensure witness node marked active (hslightdb)
|
||||
repmgrd: improve walsender disable check (Ian)
|
||||
general: ensure replication slots can be dropped by a
|
||||
replication-only user (Ian)
|
||||
|
||||
5.3.1 2022-02-15
|
||||
repmgrd: fixes for potential connection leaks (hslightdb)
|
||||
@@ -67,7 +85,7 @@
|
||||
"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)
|
||||
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
|
||||
|
||||
@@ -22,7 +22,7 @@ GIT_WORK_TREE=${repmgr_abs_srcdir}
|
||||
GIT_DIR=${repmgr_abs_srcdir}/.git
|
||||
export GIT_DIR
|
||||
export GIT_WORK_TREE
|
||||
|
||||
PG_LDFLAGS=-lcurl -ljson-c
|
||||
include $(PGXS)
|
||||
|
||||
-include ${repmgr_abs_srcdir}/Makefile.custom
|
||||
|
||||
10
Makefile.in
10
Makefile.in
@@ -32,7 +32,9 @@ DATA = \
|
||||
repmgr--5.2--5.3.sql \
|
||||
repmgr--5.3.sql \
|
||||
repmgr--5.3--5.4.sql \
|
||||
repmgr--5.4.sql
|
||||
repmgr--5.4.sql \
|
||||
repmgr--5.4--5.5.sql \
|
||||
repmgr--5.5.sql
|
||||
|
||||
REGRESS = repmgr_extension
|
||||
|
||||
@@ -66,7 +68,7 @@ REPMGR_CLIENT_OBJS = repmgr-client.o \
|
||||
repmgr-action-primary.o repmgr-action-standby.o repmgr-action-witness.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
|
||||
dbutils.o sysutils.o pgbackupapi.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
|
||||
|
||||
@@ -81,10 +83,10 @@ configfile-scan.c: configfile-scan.l
|
||||
$(REPMGR_CLIENT_OBJS): repmgr-client.h repmgr_version.h
|
||||
|
||||
repmgr: $(REPMGR_CLIENT_OBJS)
|
||||
$(CC) $(CFLAGS) $(REPMGR_CLIENT_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) -o $@$(X)
|
||||
$(CC) $(CFLAGS) $(REPMGR_CLIENT_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
|
||||
|
||||
repmgrd: $(REPMGRD_OBJS)
|
||||
$(CC) $(CFLAGS) $(REPMGRD_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) -o $@$(X)
|
||||
$(CC) $(CFLAGS) $(REPMGRD_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
|
||||
|
||||
$(REPMGR_CLIENT_OBJS): $(HEADERS)
|
||||
$(REPMGRD_OBJS): $(HEADERS)
|
||||
|
||||
23
README.md
23
README.md
@@ -7,8 +7,9 @@ replication capabilities with utilities to set up standby servers, monitor
|
||||
replication, and perform administrative tasks such as failover or switchover
|
||||
operations.
|
||||
|
||||
The most recent `repmgr` version (5.2.1) supports all PostgreSQL versions from
|
||||
9.5 to 13. PostgreSQL 9.4 is also supported, with some restrictions.
|
||||
The most recent `repmgr` version (5.5.x) supports all PostgreSQL versions from
|
||||
13 to 17. Despite it could be used with some older ones, some features might not
|
||||
be available, however, it's strongly recommended to use the latest version.
|
||||
|
||||
`repmgr` is distributed under the GNU GPL 3 and maintained by EnterpriseDB.
|
||||
|
||||
@@ -19,14 +20,6 @@ The full `repmgr` documentation is available here:
|
||||
|
||||
> [repmgr documentation](https://repmgr.org/docs/current/index.html)
|
||||
|
||||
The old `README` file for `repmgr` 3.x is available here:
|
||||
|
||||
> https://github.com/EnterpriseDB/repmgr/blob/REL3_3_STABLE/README.md
|
||||
|
||||
Note that the `repmgr` 3.x series is no longer supported and contains known bugs;
|
||||
please upgrade to the [current repmgr version](https://repmgr.org/docs/current/appendix-release-notes.html)
|
||||
as soon as possible.
|
||||
|
||||
Versions
|
||||
--------
|
||||
|
||||
@@ -48,7 +41,6 @@ Directories
|
||||
- `contrib/`: additional utilities
|
||||
- `doc/`: DocBook-based documentation files
|
||||
- `expected/`: expected regression test output
|
||||
- `scripts/`: example scripts
|
||||
- `sql/`: regression test input
|
||||
|
||||
|
||||
@@ -65,8 +57,6 @@ There is a mailing list/forum to discuss contributions or issues:
|
||||
|
||||
* https://groups.google.com/group/repmgr
|
||||
|
||||
The IRC channel #repmgr is registered with freenode.
|
||||
|
||||
Please report bugs and other issues to:
|
||||
|
||||
* https://github.com/EnterpriseDB/repmgr
|
||||
@@ -79,6 +69,13 @@ news are always welcome.
|
||||
Thanks from the repmgr core team.
|
||||
|
||||
* Ian Barwick
|
||||
* Israel Barth
|
||||
* Mario González
|
||||
* Martín Marqués
|
||||
* Gianni Ciolli
|
||||
|
||||
Past contributors:
|
||||
|
||||
* Jaime Casanova
|
||||
* Abhijit Menon-Sen
|
||||
* Simon Riggs
|
||||
|
||||
40
configdata.c
40
configdata.c
@@ -291,6 +291,46 @@ struct ConfigFileSetting config_file_settings[] =
|
||||
{},
|
||||
{}
|
||||
},
|
||||
/* pg_backupapi_backup_id*/
|
||||
{
|
||||
"pg_backupapi_backup_id",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_backupapi_backup_id },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_backupapi_backup_id) },
|
||||
{}
|
||||
},
|
||||
/* pg_backupapi_host*/
|
||||
{
|
||||
"pg_backupapi_host",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_backupapi_host },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_backupapi_host) },
|
||||
{}
|
||||
},
|
||||
/* pg_backupapi_node_name */
|
||||
{
|
||||
"pg_backupapi_node_name",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_backupapi_node_name },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_backupapi_node_name) },
|
||||
{}
|
||||
},
|
||||
/* pg_backupapi_remote_ssh_command */
|
||||
{
|
||||
"pg_backupapi_remote_ssh_command",
|
||||
CONFIG_STRING,
|
||||
{ .strptr = config_file_options.pg_backupapi_remote_ssh_command },
|
||||
{ .strdefault = "" },
|
||||
{},
|
||||
{ .strmaxlen = sizeof(config_file_options.pg_backupapi_remote_ssh_command) },
|
||||
{}
|
||||
},
|
||||
|
||||
/* =======================
|
||||
* standby follow settings
|
||||
|
||||
@@ -164,6 +164,10 @@ typedef struct
|
||||
char archive_cleanup_command[MAXLEN];
|
||||
bool use_primary_conninfo_password;
|
||||
char passfile[MAXPGPATH];
|
||||
char pg_backupapi_backup_id[NAMEDATALEN];
|
||||
char pg_backupapi_host[NAMEDATALEN];
|
||||
char pg_backupapi_node_name[NAMEDATALEN];
|
||||
char pg_backupapi_remote_ssh_command[MAXLEN];
|
||||
|
||||
/* standby promote settings */
|
||||
int promote_check_timeout;
|
||||
|
||||
24
configure.in
24
configure.in
@@ -1,6 +1,6 @@
|
||||
AC_INIT([repmgr], [5.4.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
|
||||
AC_INIT([repmgr], [5.5.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
|
||||
|
||||
AC_COPYRIGHT([Copyright (c) 2010-2021, EnterpriseDB Corporation])
|
||||
AC_COPYRIGHT([Copyright (c) 2010-2024, EnterpriseDB Corporation])
|
||||
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
||||
@@ -60,6 +60,7 @@ AC_SUBST(vpath_build)
|
||||
AC_CHECK_PROG(HAVE_GNUSED,gnused,yes,no)
|
||||
AC_CHECK_PROG(HAVE_GSED,gsed,yes,no)
|
||||
AC_CHECK_PROG(HAVE_SED,sed,yes,no)
|
||||
AC_CHECK_PROG(HAVE_FLEX,flex,yes,no)
|
||||
|
||||
if test "$HAVE_GNUSED" = yes; then
|
||||
SED=gnused
|
||||
@@ -72,6 +73,25 @@ else
|
||||
fi
|
||||
AC_SUBST(SED)
|
||||
|
||||
AS_IF([test x"$HAVE_FLEX" != x"yes"], AC_MSG_ERROR([flex should be installed first]))
|
||||
|
||||
#Checking libraries
|
||||
GENERIC_LIB_FAILED_MSG="library should be installed"
|
||||
|
||||
AC_CHECK_LIB(selinux, is_selinux_enabled, [],
|
||||
[AC_MSG_ERROR(['selinux' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(lz4, LZ4_compress_default, [],
|
||||
[AC_MSG_ERROR(['Z4' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [],
|
||||
[AC_MSG_ERROR(['xslt' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(pam, pam_start, [],
|
||||
[AC_MSG_ERROR(['pam' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(gssapi_krb5, gss_init_sec_context, [],
|
||||
[AC_MSG_ERROR([gssapi_krb5 $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES([Makefile.global])
|
||||
|
||||
112
dbutils.c
112
dbutils.c
@@ -1624,7 +1624,7 @@ get_ready_archive_files(PGconn *conn, const char *data_directory)
|
||||
|
||||
while ((arcdir_ent = readdir(arcdir)) != NULL)
|
||||
{
|
||||
struct stat statbuf;
|
||||
struct stat local_statbuf;
|
||||
char file_path[MAXPGPATH + sizeof(arcdir_ent->d_name)];
|
||||
int basenamelen = 0;
|
||||
|
||||
@@ -1634,7 +1634,7 @@ get_ready_archive_files(PGconn *conn, const char *data_directory)
|
||||
arcdir_ent->d_name);
|
||||
|
||||
/* skip non-files */
|
||||
if (stat(file_path, &statbuf) == 0 && !S_ISREG(statbuf.st_mode))
|
||||
if (stat(file_path, &local_statbuf) == 0 && !S_ISREG(local_statbuf.st_mode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1852,6 +1852,51 @@ get_wal_receiver_pid(PGconn *conn)
|
||||
/* =============================== */
|
||||
|
||||
|
||||
/*
|
||||
* Determine if the user associated with the current connection can execute CHECKPOINT command.
|
||||
* User must be a supersuer or a member of the pg_checkpoint default role (available from PostgreSQL 15).
|
||||
*/
|
||||
bool
|
||||
can_execute_checkpoint(PGconn *conn)
|
||||
{
|
||||
PQExpBufferData query;
|
||||
PGresult *res;
|
||||
bool has_pg_checkpoint_role = false;
|
||||
|
||||
/* superusers can do anything, no role check needed */
|
||||
if (is_superuser_connection(conn, NULL) == true)
|
||||
return true;
|
||||
|
||||
/* pg_checkpoint available from PostgreSQL 15 */
|
||||
if (PQserverVersion(conn) < 150000)
|
||||
return false;
|
||||
|
||||
initPQExpBuffer(&query);
|
||||
appendPQExpBufferStr(&query,
|
||||
" SELECT pg_catalog.pg_has_role('pg_checkpoint','USAGE') ");
|
||||
|
||||
res = PQexec(conn, query.data);
|
||||
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_db_error(conn, query.data,
|
||||
_("can_execute_checkpoint(): unable to query user roles"));
|
||||
}
|
||||
else
|
||||
{
|
||||
has_pg_checkpoint_role = atobool(PQgetvalue(res, 0, 0));
|
||||
}
|
||||
termPQExpBuffer(&query);
|
||||
PQclear(res);
|
||||
|
||||
return has_pg_checkpoint_role;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine if the user associated with the current connection
|
||||
* has sufficient permissions to use pg_promote function
|
||||
*/
|
||||
bool
|
||||
can_execute_pg_promote(PGconn *conn)
|
||||
{
|
||||
@@ -1913,15 +1958,47 @@ can_disable_walsender(PGconn *conn)
|
||||
if (is_superuser_connection(conn, NULL) == true)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* As of PostgreSQL 14, it is not possible for a non-superuser
|
||||
* to execute ALTER SYSTEM, so further checks are superfluous.
|
||||
* This will need modifying for PostgreSQL 15.
|
||||
*/
|
||||
log_warning(_("\"standby_disconnect_on_failover\" specified, but repmgr user is not a superuser"));
|
||||
log_detail(_("superuser permission required to disable standbys on failover"));
|
||||
PQExpBufferData query;
|
||||
PGresult *res;
|
||||
bool has_alter_system_priv = false;
|
||||
|
||||
return false;
|
||||
/* GRANT ALTER SYSTEM available from PostgreSQL 15 */
|
||||
if (PQserverVersion(conn) >= 150000)
|
||||
{
|
||||
initPQExpBuffer(&query);
|
||||
appendPQExpBufferStr(&query,
|
||||
" SELECT pg_catalog.has_parameter_privilege('wal_retrieve_retry_interval', 'ALTER SYSTEM') ");
|
||||
|
||||
res = PQexec(conn, query.data);
|
||||
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_db_error(conn, query.data,
|
||||
_("can_disable_walsender(): unable to query user parameter privileges"));
|
||||
}
|
||||
else
|
||||
{
|
||||
has_alter_system_priv = atobool(PQgetvalue(res, 0, 0));
|
||||
}
|
||||
termPQExpBuffer(&query);
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
if (has_alter_system_priv == false)
|
||||
{
|
||||
log_warning(_("\"standby_disconnect_on_failover\" specified, but repmgr user is not authorized to perform ALTER SYSTEM wal_retrieve_retry_interval"));
|
||||
|
||||
if (PQserverVersion(conn) >= 150000)
|
||||
{
|
||||
log_detail(_("superuser or ALTER SYSTEM wal_retrieve_retry_interval permission required to disable standbys on failover"));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_detail(_("superuser permission required to disable standbys on failover"));
|
||||
}
|
||||
}
|
||||
|
||||
return has_alter_system_priv;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1947,13 +2024,13 @@ connection_has_pg_monitor_role(PGconn *conn, const char *subrole)
|
||||
initPQExpBuffer(&query);
|
||||
appendPQExpBufferStr(&query,
|
||||
" SELECT CASE "
|
||||
" WHEN pg_catalog.pg_has_role('pg_monitor','MEMBER') "
|
||||
" WHEN pg_catalog.pg_has_role('pg_monitor','USAGE') "
|
||||
" THEN TRUE ");
|
||||
|
||||
if (subrole != NULL)
|
||||
{
|
||||
appendPQExpBuffer(&query,
|
||||
" WHEN pg_catalog.pg_has_role('%s','MEMBER') "
|
||||
" WHEN pg_catalog.pg_has_role('%s','USAGE') "
|
||||
" THEN TRUE ",
|
||||
subrole);
|
||||
}
|
||||
@@ -2460,7 +2537,10 @@ get_repmgr_extension_status(PGconn *conn, t_extension_versions *extversions)
|
||||
/* node management functions */
|
||||
/* ========================= */
|
||||
|
||||
/* assumes superuser connection */
|
||||
/*
|
||||
* Assumes the connection can execute CHECKPOINT command.
|
||||
* A check can be executed via 'can_execute_checkpoint' function.
|
||||
*/
|
||||
void
|
||||
checkpoint(PGconn *conn)
|
||||
{
|
||||
@@ -4616,17 +4696,17 @@ drop_replication_slot_replprot(PGconn *repl_conn, char *slot_name)
|
||||
initPQExpBuffer(&query);
|
||||
|
||||
appendPQExpBuffer(&query,
|
||||
"DROP_REPLICATION_SLOT %s",
|
||||
"DROP_REPLICATION_SLOT %s;",
|
||||
slot_name);
|
||||
|
||||
log_verbose(LOG_DEBUG, "drop_replication_slot_replprot():\n %s", query.data);
|
||||
|
||||
res = PQexec(repl_conn, query.data);
|
||||
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK || !PQntuples(res))
|
||||
{
|
||||
log_db_error(repl_conn, query.data,
|
||||
_("drop_replication_slot_sql(): unable to drop replication slot \"%s\""),
|
||||
_("drop_replication_slot_replprot(): unable to drop replication slot \"%s\""),
|
||||
slot_name);
|
||||
|
||||
success = false;
|
||||
|
||||
@@ -453,6 +453,7 @@ TimeLineHistoryEntry *get_timeline_history(PGconn *repl_conn, TimeLineID tli);
|
||||
pid_t get_wal_receiver_pid(PGconn *conn);
|
||||
|
||||
/* user/role information functions */
|
||||
bool can_execute_checkpoint(PGconn *conn);
|
||||
bool can_execute_pg_promote(PGconn *conn);
|
||||
bool can_disable_walsender(PGconn *conn);
|
||||
bool connection_has_pg_monitor_role(PGconn *conn, const char *subrole);
|
||||
|
||||
@@ -16,12 +16,96 @@
|
||||
</para>
|
||||
|
||||
<!-- remember to update the release date in ../repmgr_version.h.in -->
|
||||
|
||||
<sect1 id="release-5.5.0">
|
||||
<title>Release 5.5.0</title>
|
||||
<para><emphasis>Wed 20 November, 2024</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.5.0 is a major release.
|
||||
</para>
|
||||
<para>
|
||||
This release adds support for PostgreSQL 17
|
||||
</para>
|
||||
<para>
|
||||
Fixes warnings detected by the -Wshadow gcc flag added in PostgreSQL 16.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.4.1">
|
||||
<title id="release-current">Release 5.4.1</title>
|
||||
<para><emphasis>Tue 04 Jul, 2023</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.4.1 is a minor release providing ...
|
||||
</para>
|
||||
<sect2>
|
||||
<title>Bug fixes</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
&repmgrd;: ensure witness node metadata is updated if the primary
|
||||
node changed while the witness &repmgrd; was not running.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.4.0">
|
||||
<title>Release 5.4.0</title>
|
||||
<para><emphasis>Thu 15 March, 2023</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.4.0 is a major release.
|
||||
</para>
|
||||
<para>
|
||||
This release provides support for cloning standbys using backups taken with <ulink url="http://www.pgbarman.org">barman</ulink>
|
||||
with the use of <ulink url="https://github.com/EnterpriseDB/pg-backup-api">pg-backup-api</ulink>.
|
||||
</para>
|
||||
<para>
|
||||
Minor fixes to the documentation.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.3.3">
|
||||
<title>Release 5.3.3</title>
|
||||
<para><emphasis>Mon 17 October, 2022</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.3.3 is a minor release providing support for
|
||||
<ulink url="https://www.postgresql.org/docs/15/release-15.html">PostgreSQL 15</ulink>
|
||||
and a &repmgrd; bug fix.
|
||||
</para>
|
||||
<para>
|
||||
If upgrading from an earlier &repmgr; version, any running &repmgrd; instances should be restarted.
|
||||
</para>
|
||||
<para>
|
||||
If upgrading from &repmgr; 5.2.1 or earlier, a PostgreSQL restart <emphasis>is</emphasis> required.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>Bug fixes</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
&repmgrd;: ensure <link linkend="event-notifications">event notification</link> script is called for event
|
||||
<literal>repmgrd_upstream_disconnect</literal>. GitHub #760.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.3.2">
|
||||
<title id="release-current">Release 5.3.2</title>
|
||||
<para><emphasis>??? ??? ???, 2022</emphasis></para>
|
||||
<title>Release 5.3.2</title>
|
||||
<para><emphasis>Wed 25 May, 2022</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.3.2 is a minor release.
|
||||
</para>
|
||||
<para>
|
||||
Any running &repmgrd; instances should be restarted following this upgrade.
|
||||
</para>
|
||||
<para>
|
||||
If upgrading from &repmgr; 5.2.1 or earlier, a PostgreSQL restart <emphasis>is</emphasis> required.
|
||||
</para>
|
||||
@@ -61,6 +145,13 @@
|
||||
&repmgr; is a superuser before attempting to disable the WAL receiver.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If the &repmgr; user is a non-superuser, and a replication-only user exists,
|
||||
ensure redundant replication slots are dropped correctly even
|
||||
if the <option>-S/--superuser</option> option is not provided.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@@ -225,6 +225,109 @@ description = "Main cluster"
|
||||
</note>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="cloning-from-barman-pg_backupapi-mode" xreflabel="Using Barman through its API (pg-backup-api)">
|
||||
<title>Using Barman through its API (pg-backup-api)</title>
|
||||
<indexterm>
|
||||
<primary>cloning</primary>
|
||||
<secondary>pg-backup-api</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
You can find information on how to install and setup pg-backup-api in
|
||||
<ulink url="https://www.enterprisedb.com/docs/supported-open-source/barman/pg-backup-api/">the pg-backup-api
|
||||
documentation</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This mode (`pg-backupapi`) was introduced in v5.4.0 as a way to further integrate with Barman letting Barman
|
||||
handle the restore. This also reduces the ssh keys that need to share between the backup and postgres nodes.
|
||||
As long as you have access to the API service by HTTP calls, you could perform recoveries right away.
|
||||
You just need to instruct Barman through the API which backup you need and on which node the backup needs to
|
||||
to be restored on.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In order to enable <literal>pg_backupapi mode</literal> support for <command>repmgr standby clone</command>,
|
||||
you need the following lines in repmgr.conf:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem><para>pg_backupapi_host: Where pg-backup-api is hosted</para></listitem>
|
||||
<listitem><para>pg_backupapi_node_name: Name of the server as understood by Barman</para></listitem>
|
||||
<listitem><para>pg_backupapi_remote_ssh_command: How Barman will be connecting as to the node</para></listitem>
|
||||
<listitem><para>pg_backupapi_backup_id: ID of the existing backup you need to restore</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
This is an example of how repmgr.conf would look like:
|
||||
|
||||
<programlisting>
|
||||
pg_backupapi_host = '192.168.122.154'
|
||||
pg_backupapi_node_name = 'burrito'
|
||||
pg_backupapi_remote_ssh_command = 'ssh john_doe@192.168.122.1'
|
||||
pg_backupapi_backup_id = '20230223T093201'
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>pg_backupapi_host</literal> is the variable name that enables this mode, and when you set it,
|
||||
all the rest of the above variables are required. Also, remember that this service is just an interface
|
||||
between Barman and repmgr, hence if something fails during a recovery, you should check Barman's logs upon
|
||||
why the process couldn't finish properly.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<simpara>
|
||||
Despite in Barman you can define shortcuts like "lastest" or "oldest", they are not supported for the
|
||||
time being in pg-backup-api. These shortcuts will be supported in a future release.
|
||||
</simpara>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
This is a real example of repmgr's output cloning with the API. Note that during this operation, we stopped
|
||||
the service for a little while and repmgr had to retry but that doesn't affect the final outcome. The primary
|
||||
is listening on localhost's port 6001:
|
||||
|
||||
<programlisting>
|
||||
$ repmgr -f ~/nodes/node_3/repmgr.conf standby clone -U repmgr -p 6001 -h localhost
|
||||
NOTICE: destination directory "/home/mario/nodes/node_3/data" provided
|
||||
INFO: Attempting to use `pg_backupapi` new restore mode
|
||||
INFO: connecting to source node
|
||||
DETAIL: connection string is: user=repmgr port=6001 host=localhost
|
||||
DETAIL: current installation size is 8541 MB
|
||||
DEBUG: 1 node records returned by source node
|
||||
DEBUG: connecting to: "user=repmgr dbname=repmgr host=localhost port=6001 connect_timeout=2 fallback_application_name=repmgr options=-csearch_path="
|
||||
DEBUG: upstream_node_id determined as 1
|
||||
INFO: Attempting to use `pg_backupapi` new restore mode
|
||||
INFO: replication slot usage not requested; no replication slot will be set up for this standby
|
||||
NOTICE: starting backup (using pg_backupapi)...
|
||||
INFO: Success creating the task: operation id '20230309T150647'
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
Incorrect reply received for that operation ID.
|
||||
INFO: Retrying...
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status DONE
|
||||
NOTICE: standby clone (from pg_backupapi) complete
|
||||
NOTICE: you can now start your PostgreSQL server
|
||||
HINT: for example: pg_ctl -D /home/mario/nodes/node_3/data start
|
||||
HINT: after starting the server, you need to register this standby with "repmgr standby register"
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
</sect2> <!--END cloning-from-barman-pg_backupapi-mode !-->
|
||||
</sect1>
|
||||
|
||||
<sect1 id="cloning-replication-slots" xreflabel="Cloning and replication slots">
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<note>
|
||||
<simpara>
|
||||
This section documents a subset of optional configuration settings; for a full
|
||||
for a full and annotated view of all configuration options see the
|
||||
see the <ulink url="https://raw.githubusercontent.com/EnterpriseDB/repmgr/master/repmgr.conf.sample">sample repmgr.conf file</ulink>
|
||||
and annotated view of all configuration options see the
|
||||
<ulink url="https://raw.githubusercontent.com/EnterpriseDB/repmgr/master/repmgr.conf.sample">sample repmgr.conf file</ulink>
|
||||
</simpara>
|
||||
</note>
|
||||
|
||||
|
||||
@@ -79,6 +79,10 @@
|
||||
Alternatively the meta-role <varname>pg_monitor</varname> can be granted, which includes membership
|
||||
of the above predefined roles.
|
||||
</para>
|
||||
<para>
|
||||
PostgreSQL 15 introduced the <varname>pg_checkpoint</varname> predefined role which allows a
|
||||
non-superuser &repmgr; database user to perform a CHECKPOINT command.
|
||||
</para>
|
||||
<para>
|
||||
Membership of these roles can be granted with e.g. <command>GRANT pg_read_all_stats TO repmgr</command>.
|
||||
</para>
|
||||
@@ -148,6 +152,8 @@
|
||||
<link linkend="repmgr-standby-switchover">repmgr standby switchover</link>. This can only
|
||||
be executed by a superuser; if the &repmgr; user is not a superuser,
|
||||
the <option>-S</option>/<option>--superuser</option> should be used.
|
||||
From PostgreSQL 15 the <varname>pg_checkpoint</varname> predefined role removes the need of
|
||||
superuser permissions to perform <command>CHECKPOINT</command> command.
|
||||
</simpara>
|
||||
<simpara>
|
||||
If &repmgr; is not able to execute <command>CHECKPOINT</command>,
|
||||
@@ -159,8 +165,10 @@
|
||||
<simpara>
|
||||
The <command>ALTER SYSTEM</command> is executed by &repmgrd; if
|
||||
<varname>standby_disconnect_on_failover</varname> is set to <literal>true</literal> in
|
||||
<filename>repmgr.conf</filename>. <command>ALTER SYSTEM</command> can only be executed by
|
||||
<filename>repmgr.conf</filename>. Until PostgreSQL 14 <command>ALTER SYSTEM</command> can only be executed by
|
||||
a superuser; if the &repmgr; user is not a superuser, this functionality will not be available.
|
||||
From PostgreSQL 15 a specific ALTER SYSTEM privilege can be granted with e.g.
|
||||
<command>GRANT ALTER SYSTEM ON PARAMETER wal_retrieve_retry_interval TO repmgr</command>.
|
||||
</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
@@ -108,10 +108,31 @@
|
||||
<entry>
|
||||
Supported PostgreSQL versions
|
||||
</entry>
|
||||
<entry>
|
||||
Notes
|
||||
</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
&repmgr; 5.4
|
||||
</entry>
|
||||
<entry>
|
||||
(dev)
|
||||
</entry>
|
||||
<entry>
|
||||
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
|
||||
</entry>
|
||||
<entry>
|
||||
9.4, 9.5, 9.6, 10, 11, 12, 13, 15
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
&repmgr; 5.3
|
||||
@@ -123,7 +144,10 @@
|
||||
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
|
||||
</entry>
|
||||
<entry>
|
||||
9.4, 9.5, 9.6, 10, 11, 12, 13, 14
|
||||
9.4, 9.5, 9.6, 10, 11, 12, 13, 14, 15
|
||||
</entry>
|
||||
<entry>
|
||||
PostgreSQL 15 supported from &repmgr; 5.3.3
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
@@ -140,6 +164,9 @@
|
||||
<entry>
|
||||
9.4, 9.5, 9.6, 10, 11, 12, 13
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -155,6 +182,9 @@
|
||||
<entry>
|
||||
9.3, 9.4, 9.5, 9.6, 10, 11, 12
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -170,6 +200,9 @@
|
||||
<entry>
|
||||
9.3, 9.4, 9.5, 9.6, 10, 11, 12
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
|
||||
@@ -186,6 +219,9 @@
|
||||
<entry>
|
||||
9.3, 9.4, 9.5, 9.6, 10, 11
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -201,6 +237,9 @@
|
||||
<entry>
|
||||
9.3, 9.4, 9.5, 9.6
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -216,6 +255,9 @@
|
||||
<entry>
|
||||
9.0, 9.1, 9.2, 9.3, 9.4
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
|
||||
|
||||
@@ -73,16 +73,16 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--force-rewind[=/path/to/pg_rewind]</option></term>
|
||||
<term><option>--force-rewind</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Execute <application>pg_rewind</application>.
|
||||
</para>
|
||||
<para>
|
||||
It is only necessary to provide the <application>pg_rewind</application> path
|
||||
if using PostgreSQL 9.4, and <application>pg_rewind</application>
|
||||
is not installed in the PostgreSQL <filename>bin</filename> directory.
|
||||
See <xref linkend="repmgr-node-rejoin-pg-rewind"/> for more details on using
|
||||
<application>pg_rewind</application>.
|
||||
</para>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -261,8 +261,6 @@
|
||||
<para>
|
||||
<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.4.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
@@ -271,6 +269,10 @@
|
||||
data checksums were enabled when the cluster was initialized. See the
|
||||
<ulink url="https://www.postgresql.org/docs/current/app-pgrewind.html"><command>pg_rewind</command> documentation</ulink> for details.
|
||||
</para>
|
||||
<para>
|
||||
Additionally, <varname>full_page_writes</varname> must be enabled; this is the default and
|
||||
normally should never be disabled.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
@@ -382,6 +384,29 @@
|
||||
DETAIL: node 2 is now attached to node 3</programlisting>
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id="repmgr-node-rejoin-postgresql-94" xreflabel="pg_rewind and PostgreSQL 9.4">
|
||||
|
||||
<title><command>pg_rewind</command> and PostgreSQL 9.4</title>
|
||||
|
||||
<indexterm>
|
||||
<primary>pg_rewind</primary>
|
||||
<secondary>PostgreSQL 9.4</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
<application>pg_rewind</application> is available in PostgreSQL 9.5 and later as part of the core distribution.
|
||||
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.4 version of <application>pg_rewind</application> requires the PostgreSQL
|
||||
source code.
|
||||
</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="repmgr-node-rejoin-caveats" xreflabel="Caveats">
|
||||
@@ -426,7 +451,7 @@
|
||||
|
||||
<warning>
|
||||
<para>
|
||||
In all current PostgreSQL versions (as of September 2020), <application>pg_rewind</application>
|
||||
In all PostgreSQL released before February 2021, <application>pg_rewind</application>
|
||||
contains a corner-case bug which affects standbys in a very specific situation.
|
||||
</para>
|
||||
<para>
|
||||
@@ -456,8 +481,7 @@
|
||||
<para>
|
||||
Currently it is not possible to resolve this situation using <application>pg_rewind</application>.
|
||||
A <ulink url="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=2b4f3130382fe2f8705863e4d38589d4d69cd695">patch</ulink>
|
||||
has been successfully submitted and will be included the next PostgreSQL minor release round, scheduled for
|
||||
February 2021.
|
||||
was submitted and is included in all PostgreSQL versions released in February 2021 or later.
|
||||
</para>
|
||||
<para>
|
||||
As a workaround, start the primary server the standby was previously attached to,
|
||||
@@ -475,7 +499,7 @@
|
||||
<refsect1>
|
||||
<title>See also</title>
|
||||
<para>
|
||||
<xref linkend="repmgr-standby-follow"/>
|
||||
<xref linkend="repmgr-standby-follow"/>, <xref linkend="repmgr-standby-switchover"/>
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
</para>
|
||||
<para>
|
||||
Note that a superuser connection is required to be able to execute the
|
||||
<command>CHECKPOINT</command> command.
|
||||
<command>CHECKPOINT</command> command. From PostgreSQL 15 the <varname>pg_checkpoint</varname>
|
||||
predefined role removes the need for superuser permissions to perform <command>CHECKPOINT</command> command.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -79,7 +79,8 @@
|
||||
<para>
|
||||
Note that <command>CHECKPOINT</command> requires database superuser permissions to execute.
|
||||
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> option.
|
||||
provided with the <option>-S</option>/<option>--superuser</option> option. From PostgreSQL 15 the <varname>pg_checkpoint</varname>
|
||||
predefined role removes the need for superuser permissions to perform <command>CHECKPOINT</command> command.
|
||||
</para>
|
||||
<para>
|
||||
If &repmgr; is unable to execute the <command>CHECKPOINT</command> command, the switchover
|
||||
@@ -154,9 +155,12 @@
|
||||
<para>
|
||||
Use <application>pg_rewind</application> to reintegrate the old primary if necessary
|
||||
(and the prerequisites for using <application>pg_rewind</application> are met).
|
||||
</para>
|
||||
<para>
|
||||
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"/>.
|
||||
provide its full path. For more details see also <xref linkend="switchover-pg-rewind"/>
|
||||
and <xref linkend="repmgr-node-rejoin-pg-rewind"/>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<abstract>
|
||||
<para>
|
||||
This is the official documentation of &repmgr; &repmgrversion; for
|
||||
use with PostgreSQL 9.4 - PostgreSQL 14.
|
||||
use with PostgreSQL 12 - PostgreSQL 17.
|
||||
</para>
|
||||
<para>
|
||||
&repmgr; is being continually developed and we strongly recommend using the
|
||||
|
||||
@@ -279,7 +279,9 @@
|
||||
<note>
|
||||
<para>
|
||||
<option>standby_disconnect_on_failover</option> is available with PostgreSQL 9.5 and later.
|
||||
Additionally this requires that the <literal>repmgr</literal> database user is a superuser.
|
||||
Until PostgreSQL 14 this requires that the <literal>repmgr</literal> database user is a superuser.
|
||||
From PostgreSQL 15 a specific ALTER SYSTEM privilege can be granted to the <literal>repmgr</literal> database
|
||||
user with e.g. <command>GRANT ALTER SYSTEM ON PARAMETER wal_retrieve_retry_interval TO repmgr</command>.
|
||||
</para>
|
||||
</note>
|
||||
<para>
|
||||
|
||||
@@ -345,14 +345,18 @@
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
Indicates a preferred priority (default: <literal>100</literal>) for promoting nodes;
|
||||
a value of zero prevents the node being promoted to primary.
|
||||
Indicates a preferred priority (default: <literal>100</literal>) for promoting nodes.
|
||||
</para>
|
||||
<para>
|
||||
Note that the priority setting is only applied if two or more nodes are
|
||||
determined as promotion candidates; in that case the node with the
|
||||
higher priority is selected.
|
||||
</para>
|
||||
<para>
|
||||
A value of zero will always prevent the node being promoted to primary, even if there
|
||||
is no other promotion candidate.
|
||||
</para>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
option to execute custom scripts ("<link linkend="event-notifications">event notifications</link>
|
||||
option to execute custom scripts ("<link linkend="event-notifications">event notifications</link>")
|
||||
at different points in the failover sequence
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
@@ -242,21 +242,12 @@
|
||||
</simpara>
|
||||
</note>
|
||||
<para>
|
||||
For more details on <application>pg_rewind</application>, see:
|
||||
For more details on <application>pg_rewind</application>, see section <xref linkend="repmgr-node-rejoin-pg-rewind"/>
|
||||
in the <link linkend="repmgr-node-rejoin"><command>repmgr node rejoin</command></link> documentation and
|
||||
the PostgreSQL documentation at
|
||||
<ulink url="https://www.postgresql.org/docs/current/app-pgrewind.html">https://www.postgresql.org/docs/current/app-pgrewind.html</ulink>.
|
||||
</para>
|
||||
<para>
|
||||
<application>pg_rewind</application> has been part of the core PostgreSQL distribution since
|
||||
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.4 version of <application>pg_rewind</application> requires the PostgreSQL
|
||||
source code.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
|
||||
@@ -71,6 +71,12 @@
|
||||
<secondary>minor release</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
A minor release upgrade involves updating &repmgr; from one minor release to another
|
||||
minor release within the same major release (e.g. <literal>5.3.1</literal> to <literal>5.3.2</literal>).
|
||||
An upgrade between minor releases of differing major releases (e.g. <literal>5.2.1</literal> to <literal>5.3.2</literal>)
|
||||
is a <link linkend="upgrading-major-version">major upgrade</link>.
|
||||
</para>
|
||||
<para>
|
||||
The process for installing minor version upgrades is quite straightforward:
|
||||
|
||||
@@ -105,15 +111,17 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A PostgreSQL restart is <emphasis>not</emphasis> required for minor version upgrades.
|
||||
A PostgreSQL restart is usually <emphasis>not</emphasis> required for minor version upgrades
|
||||
within the same major version (e.g. <literal>5.3.1</literal> to <literal>5.3.2</literal>).
|
||||
Be sure to check the <link linkend="appendix-release-notes">release notes</link>.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
The same &repmgr; "major version" (e.g. <literal>4.2</literal>) must be
|
||||
installed on all nodes in the replication cluster. While it's possible to have differing
|
||||
&repmgr; "minor versions" (e.g. <literal>4.2.1</literal>) on different nodes,
|
||||
we strongly recommend updating all nodes to the latest minor version.
|
||||
The same &repmgr; "major version" (e.g. <literal>5.3</literal>) must be
|
||||
installed on all nodes in the replication cluster. While it's possible to have differing
|
||||
&repmgr; "minor versions" (e.g. <literal>5.3.1</literal> and <literal>5.3.2</literal>)
|
||||
on different nodes, we strongly recommend updating all nodes to the latest minor version.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
|
||||
@@ -49,5 +49,6 @@
|
||||
#define ERR_NODE_STATUS 25
|
||||
#define ERR_REPMGRD_PAUSE 26
|
||||
#define ERR_REPMGRD_SERVICE 27
|
||||
#define ERR_PGBACKUPAPI_SERVICE 28
|
||||
|
||||
#endif /* _ERRCODE_H_ */
|
||||
|
||||
147
pgbackupapi.c
Normal file
147
pgbackupapi.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* pgbackupapi.c
|
||||
* Copyright (c) EnterpriseDB Corporation, 2010-2021
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "pgbackupapi.h"
|
||||
|
||||
|
||||
size_t receive_operations_cb(void *content, size_t size, size_t nmemb, char *buffer) {
|
||||
short int max_chars_to_copy = MAX_BUFFER_LENGTH -2;
|
||||
short int i = 0;
|
||||
int operation_length = 0;
|
||||
json_object *value;
|
||||
|
||||
json_object *root = json_tokener_parse(content);
|
||||
json_object *operations = json_object_object_get(root, "operations");
|
||||
|
||||
operation_length = strlen(json_object_get_string(operations));
|
||||
if (operation_length < max_chars_to_copy) {
|
||||
max_chars_to_copy = operation_length;
|
||||
}
|
||||
|
||||
strncpy(buffer, json_object_get_string(operations), max_chars_to_copy);
|
||||
|
||||
fprintf(stdout, "Success! The following operations were found\n");
|
||||
for (i=0; i<json_object_array_length(operations); i++) {
|
||||
value = json_object_array_get_idx(operations, i);
|
||||
printf("%s\n", json_object_get_string(value));
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
char * define_base_url(operation_task *task) {
|
||||
char *format = "http://%s:7480/servers/%s/operations";
|
||||
char *url = malloc(MAX_BUFFER_LENGTH);
|
||||
|
||||
snprintf(url, MAX_BUFFER_LENGTH-1, format, task->host, task->node_name);
|
||||
|
||||
//`url` is freed on the function that called this
|
||||
return url;
|
||||
}
|
||||
|
||||
CURLcode get_operations_on_server(CURL *curl, operation_task *task) {
|
||||
char buffer[MAX_BUFFER_LENGTH];
|
||||
char *url = define_base_url(task);
|
||||
CURLcode ret;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_operations_cb);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
ret = curl_easy_perform(curl);
|
||||
free(url);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t receive_operation_id(void *content, size_t size, size_t nmemb, char *buffer) {
|
||||
json_object *root = json_tokener_parse(content);
|
||||
json_object *operation = json_object_object_get(root, "operation_id");
|
||||
|
||||
if (operation != NULL) {
|
||||
strncpy(buffer, json_object_get_string(operation), MAX_BUFFER_LENGTH-2);
|
||||
}
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
||||
CURLcode create_new_task(CURL *curl, operation_task *task) {
|
||||
PQExpBufferData payload;
|
||||
char *url = define_base_url(task);
|
||||
CURLcode ret;
|
||||
json_object *root = json_object_new_object();
|
||||
struct curl_slist *chunk = NULL;
|
||||
|
||||
json_object_object_add(root, "operation_type", json_object_new_string(task->operation_type));
|
||||
json_object_object_add(root, "backup_id", json_object_new_string(task->backup_id));
|
||||
json_object_object_add(root, "remote_ssh_command", json_object_new_string(task->remote_ssh_command));
|
||||
json_object_object_add(root, "destination_directory", json_object_new_string(task->destination_directory));
|
||||
|
||||
initPQExpBuffer(&payload);
|
||||
appendPQExpBufferStr(&payload, json_object_to_json_string(root));
|
||||
|
||||
chunk = curl_slist_append(chunk, "Content-type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.data);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_operation_id);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, task->operation_id);
|
||||
ret = curl_easy_perform(curl);
|
||||
free(url);
|
||||
termPQExpBuffer(&payload);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t receive_operation_status(void *content, size_t size, size_t nmemb, char *buffer) {
|
||||
json_object *root = json_tokener_parse(content);
|
||||
json_object *status = json_object_object_get(root, "status");
|
||||
if (status != NULL) {
|
||||
strncpy(buffer, json_object_get_string(status), MAX_BUFFER_LENGTH-2);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Incorrect reply received for that operation ID.\n");
|
||||
strcpy(buffer, "\0");
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
CURLcode get_status_of_operation(CURL *curl, operation_task *task) {
|
||||
CURLcode ret;
|
||||
char *url = define_base_url(task);
|
||||
|
||||
strcat(url, "/");
|
||||
strcat(url, task->operation_id);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_operation_status);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, task->operation_status);
|
||||
|
||||
ret = curl_easy_perform(curl);
|
||||
free(url);
|
||||
|
||||
return ret;
|
||||
}
|
||||
46
pgbackupapi.h
Normal file
46
pgbackupapi.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* pgbackupapi.h
|
||||
* Copyright (c) EnterpriseDB Corporation, 2010-2021
|
||||
*
|
||||
* 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 <curl/curl.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
typedef struct operation_task {
|
||||
char *backup_id;
|
||||
char *destination_directory;
|
||||
char *operation_type;
|
||||
char *operation_id;
|
||||
char *operation_status;
|
||||
char *remote_ssh_command;
|
||||
char *host;
|
||||
char *node_name;
|
||||
} operation_task;
|
||||
|
||||
//Default simplebuffer size in most of operations
|
||||
#define MAX_BUFFER_LENGTH 72
|
||||
|
||||
//Callbacks to send/receive data from pg-backup-api endpoints
|
||||
size_t receive_operations_cb(void *content, size_t size, size_t nmemb, char *buffer);
|
||||
size_t receive_operation_id(void *content, size_t size, size_t nmemb, char *buffer);
|
||||
size_t receive_operation_status(void *content, size_t size, size_t nmemb, char *buffer);
|
||||
|
||||
//Functions that implement the logic and know what to do and how to comunnicate wuth the API
|
||||
CURLcode get_operations_on_server(CURL *curl, operation_task *task);
|
||||
CURLcode create_new_task(CURL *curl, operation_task *task);
|
||||
CURLcode get_status_of_operation(CURL *curl, operation_task *task);
|
||||
|
||||
//Helper to make simpler to read the handler where we set the URL
|
||||
char * define_base_url(operation_task *task);
|
||||
2
repmgr--5.4--5.5.sql
Normal file
2
repmgr--5.4--5.5.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- complain if script is sourced in psql, rather than via CREAT EXTENSION
|
||||
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit
|
||||
191
repmgr--5.5.sql
Normal file
191
repmgr--5.5.sql
Normal file
@@ -0,0 +1,191 @@
|
||||
-- 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', 'repmgr_set_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_local_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'repmgr_get_local_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_set_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'repmgr_standby_set_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION standby_get_last_updated()
|
||||
RETURNS TIMESTAMP WITH TIME ZONE
|
||||
AS 'MODULE_PATHNAME', 'repmgr_standby_get_last_updated'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_last_seen(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgr_set_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_last_seen()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'repmgr_get_upstream_last_seen'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_upstream_node_id()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'repmgr_get_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION set_upstream_node_id(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgr_set_upstream_node_id'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
/* failover functions */
|
||||
|
||||
CREATE FUNCTION notify_follow_primary(INT)
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgr_notify_follow_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION get_new_primary()
|
||||
RETURNS INT
|
||||
AS 'MODULE_PATHNAME', 'repmgr_get_new_primary'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
CREATE FUNCTION reset_voting_status()
|
||||
RETURNS VOID
|
||||
AS 'MODULE_PATHNAME', 'repmgr_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', 'repmgr_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
|
||||
);
|
||||
@@ -394,7 +394,6 @@ do_node_status(void)
|
||||
if (node_info.inactive_replication_slots > 0)
|
||||
{
|
||||
KeyValueList inactive_replication_slots = {NULL, NULL};
|
||||
KeyValueListCell *cell = NULL;
|
||||
|
||||
(void) get_inactive_replication_slots(conn, &inactive_replication_slots);
|
||||
|
||||
@@ -524,8 +523,6 @@ do_node_status(void)
|
||||
if (node_info.inactive_replication_slots)
|
||||
{
|
||||
KeyValueList inactive_replication_slots = {NULL, NULL};
|
||||
KeyValueListCell *cell = NULL;
|
||||
|
||||
(void) get_inactive_replication_slots(conn, &inactive_replication_slots);
|
||||
for (cell = inactive_replication_slots.head; cell; cell = cell->next)
|
||||
{
|
||||
@@ -2365,18 +2362,25 @@ do_node_service(void)
|
||||
conn = establish_db_connection_by_params(&source_conninfo, true);
|
||||
}
|
||||
|
||||
if (is_superuser_connection(conn, NULL) == false)
|
||||
if (can_execute_checkpoint(conn) == false)
|
||||
{
|
||||
if (runtime_options.dry_run == true)
|
||||
{
|
||||
log_warning(_("a CHECKPOINT would be issued here but no superuser connection is available"));
|
||||
log_warning(_("a CHECKPOINT would be issued here but no authorized connection is available"));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_warning(_("a superuser connection is required to issue a CHECKPOINT"));
|
||||
log_warning(_("an authorized connection is required to issue a CHECKPOINT"));
|
||||
}
|
||||
|
||||
log_hint(_("provide a superuser with -S/--superuser"));
|
||||
if (PQserverVersion(conn) >= 150000)
|
||||
{
|
||||
log_hint(_("provide a superuser with -S/--superuser or grant pg_checkpoint role to repmgr user"));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_hint(_("provide a superuser with -S/--superuser"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2539,6 +2543,7 @@ do_node_rejoin(void)
|
||||
int server_version_num = UNKNOWN_SERVER_VERSION_NUM;
|
||||
bool hide_standby_signal = false;
|
||||
|
||||
KeyValueListCell *cell = NULL;
|
||||
PQExpBufferData command;
|
||||
PQExpBufferData command_output;
|
||||
PQExpBufferData follow_output;
|
||||
@@ -2958,7 +2963,7 @@ do_node_rejoin(void)
|
||||
else
|
||||
{
|
||||
while ((slotdir_ent = readdir(slotdir)) != NULL) {
|
||||
struct stat statbuf;
|
||||
struct stat local_statbuf;
|
||||
PQExpBufferData slotdir_ent_path;
|
||||
|
||||
if (strcmp(slotdir_ent->d_name, ".") == 0 || strcmp(slotdir_ent->d_name, "..") == 0)
|
||||
@@ -2971,7 +2976,7 @@ do_node_rejoin(void)
|
||||
slotdir_path.data,
|
||||
slotdir_ent->d_name);
|
||||
|
||||
if (stat(slotdir_ent_path.data, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
|
||||
if (stat(slotdir_ent_path.data, &local_statbuf) == 0 && !S_ISDIR(local_statbuf.st_mode))
|
||||
{
|
||||
termPQExpBuffer(&slotdir_ent_path);
|
||||
continue;
|
||||
@@ -3102,7 +3107,6 @@ do_node_rejoin(void)
|
||||
else
|
||||
{
|
||||
KeyValueList inactive_replication_slots = {NULL, NULL};
|
||||
KeyValueListCell *cell = NULL;
|
||||
int inactive_count = 0;
|
||||
PQExpBufferData slotinfo;
|
||||
|
||||
|
||||
@@ -235,6 +235,38 @@ do_primary_register(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void reset_node_info(t_node_info *info)
|
||||
{
|
||||
info->node_id = NODE_NOT_FOUND;
|
||||
info->upstream_node_id = NO_UPSTREAM_NODE;
|
||||
info->type = UNKNOWN;
|
||||
info->node_name[0] = '\0';
|
||||
info->upstream_node_name[0] = '\0';
|
||||
info->conninfo[0] = '\0';
|
||||
info->repluser[0] = '\0';
|
||||
info->priority = DEFAULT_PRIORITY;
|
||||
info->active = true;
|
||||
info->slot_name[0] = '\0';
|
||||
info->config_file[0] = '\0';
|
||||
info->last_wal_receive_lsn = InvalidXLogRecPtr;
|
||||
info->node_status = NODE_STATUS_UNKNOWN;
|
||||
info->recovery_type = RECTYPE_UNKNOWN;
|
||||
info->monitoring_state = MS_NORMAL;
|
||||
info->conn = NULL;
|
||||
info->details[0] = '\0';
|
||||
info->reachable = true;
|
||||
info->attached = true;
|
||||
/* various statistics */
|
||||
info->max_wal_senders = -1;
|
||||
info->attached_wal_receivers = -1;
|
||||
info->max_replication_slots = -1;
|
||||
info->total_replication_slots = -1;
|
||||
info->active_replication_slots = -1;
|
||||
info->inactive_replication_slots = -1;
|
||||
info->replication_info = NULL;
|
||||
|
||||
strlcpy(info->location, DEFAULT_LOCATION, sizeof(info->location));
|
||||
}
|
||||
|
||||
/*
|
||||
* do_primary_unregister()
|
||||
@@ -436,7 +468,7 @@ do_primary_unregister(void)
|
||||
}
|
||||
else if (recovery_type == RECTYPE_PRIMARY)
|
||||
{
|
||||
t_node_info primary_node_info = T_NODE_INFO_INITIALIZER;
|
||||
reset_node_info(&primary_node_info);
|
||||
bool primary_record_found = false;
|
||||
|
||||
primary_record_found = get_primary_node_record(primary_conn, &primary_node_info);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "dirutil.h"
|
||||
@@ -29,7 +30,7 @@
|
||||
|
||||
#include "repmgr-client-global.h"
|
||||
#include "repmgr-action-standby.h"
|
||||
|
||||
#include "pgbackupapi.h"
|
||||
|
||||
typedef struct TablespaceDataListCell
|
||||
{
|
||||
@@ -113,6 +114,7 @@ static void check_recovery_type(PGconn *conn);
|
||||
static void initialise_direct_clone(t_node_info *local_node_record, t_node_info *upstream_node_record);
|
||||
static int run_basebackup(t_node_info *node_record);
|
||||
static int run_file_backup(t_node_info *node_record);
|
||||
static int run_pg_backupapi(t_node_info *node_record);
|
||||
|
||||
static void copy_configuration_files(bool delete_after_copy);
|
||||
|
||||
@@ -533,7 +535,7 @@ do_standby_clone(void)
|
||||
|
||||
if (external_config_files == true)
|
||||
{
|
||||
int r;
|
||||
r = 0;
|
||||
PQExpBufferData msg;
|
||||
|
||||
initPQExpBuffer(&msg);
|
||||
@@ -687,19 +689,18 @@ do_standby_clone(void)
|
||||
exit(SUCCESS);
|
||||
}
|
||||
|
||||
if (mode != barman)
|
||||
{
|
||||
initialise_direct_clone(&local_node_record, &upstream_node_record);
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case pg_basebackup:
|
||||
initialise_direct_clone(&local_node_record, &upstream_node_record);
|
||||
log_notice(_("starting backup (using pg_basebackup)..."));
|
||||
break;
|
||||
case barman:
|
||||
log_notice(_("retrieving backup from Barman..."));
|
||||
break;
|
||||
case pg_backupapi:
|
||||
log_notice(_("starting backup (using pg_backupapi)..."));
|
||||
break;
|
||||
default:
|
||||
/* should never reach here */
|
||||
log_error(_("unknown clone mode"));
|
||||
@@ -721,6 +722,9 @@ do_standby_clone(void)
|
||||
case barman:
|
||||
r = run_file_backup(&local_node_record);
|
||||
break;
|
||||
case pg_backupapi:
|
||||
r = run_pg_backupapi(&local_node_record);
|
||||
break;
|
||||
default:
|
||||
/* should never reach here */
|
||||
log_error(_("unknown clone mode"));
|
||||
@@ -758,7 +762,7 @@ do_standby_clone(void)
|
||||
if (mode == pg_basebackup && runtime_options.verify_backup == true)
|
||||
{
|
||||
PQExpBufferData command;
|
||||
int r;
|
||||
r = 0;
|
||||
struct stat st;
|
||||
|
||||
initPQExpBuffer(&command);
|
||||
@@ -814,7 +818,6 @@ do_standby_clone(void)
|
||||
}
|
||||
|
||||
/* Write the recovery.conf file */
|
||||
|
||||
if (create_recovery_file(&local_node_record,
|
||||
&recovery_conninfo,
|
||||
source_server_version_num,
|
||||
@@ -846,6 +849,9 @@ do_standby_clone(void)
|
||||
case barman:
|
||||
log_notice(_("standby clone (from Barman) complete"));
|
||||
break;
|
||||
case pg_backupapi:
|
||||
log_notice(_("standby clone (from pg_backupapi) complete"));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -937,6 +943,9 @@ do_standby_clone(void)
|
||||
case barman:
|
||||
appendPQExpBufferStr(&event_details, "barman");
|
||||
break;
|
||||
case pg_backupapi:
|
||||
appendPQExpBufferStr(&event_details, "pg_backupapi");
|
||||
break;
|
||||
}
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
@@ -3918,7 +3927,6 @@ do_standby_switchover(void)
|
||||
|
||||
if (PQserverVersion(local_conn) < 130000 && is_wal_replay_paused(local_conn, false) == true)
|
||||
{
|
||||
ReplInfo replication_info;
|
||||
init_replication_info(&replication_info);
|
||||
|
||||
if (get_replication_info(local_conn, STANDBY, &replication_info) == false)
|
||||
@@ -4775,7 +4783,7 @@ do_standby_switchover(void)
|
||||
{
|
||||
NodeInfoListCell *cell = NULL;
|
||||
ItemList repmgrd_connection_errors = {NULL, NULL};
|
||||
int i = 0;
|
||||
i = 0;
|
||||
int unreachable_node_count = 0;
|
||||
|
||||
get_all_node_records(local_conn, &all_nodes);
|
||||
@@ -5090,8 +5098,6 @@ do_standby_switchover(void)
|
||||
/* database server could not be contacted */
|
||||
if (ping_res == PQPING_NO_RESPONSE || ping_res == PQPING_NO_ATTEMPT)
|
||||
{
|
||||
bool command_success;
|
||||
|
||||
/*
|
||||
* remote server can't be contacted at protocol level - that
|
||||
* doesn't necessarily mean it's shut down, so we'll ask its
|
||||
@@ -5279,7 +5285,7 @@ do_standby_switchover(void)
|
||||
checkpoint_conn = superuser_conn;
|
||||
}
|
||||
|
||||
if (is_superuser_connection(checkpoint_conn, NULL) == true)
|
||||
if (can_execute_checkpoint(checkpoint_conn) == true)
|
||||
{
|
||||
log_notice(_("issuing CHECKPOINT on node \"%s\" (ID: %i) "),
|
||||
config_file_options.node_name,
|
||||
@@ -5288,7 +5294,16 @@ do_standby_switchover(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
log_warning(_("no superuser connection available, unable to issue CHECKPOINT"));
|
||||
log_warning(_("no authorized connection available, unable to issue CHECKPOINT"));
|
||||
|
||||
if (PQserverVersion(local_conn) >= 150000)
|
||||
{
|
||||
log_hint(_("provide a superuser with -S/--superuser or grant pg_checkpoint role to repmgr user"));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_hint(_("provide a superuser with -S/--superuser"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5660,7 +5675,7 @@ do_standby_switchover(void)
|
||||
{
|
||||
ItemList repmgrd_unpause_errors = {NULL, NULL};
|
||||
NodeInfoListCell *cell = NULL;
|
||||
int i = 0;
|
||||
i = 0;
|
||||
int error_node_count = 0;
|
||||
|
||||
for (cell = all_nodes.head; cell; cell = cell->next)
|
||||
@@ -6495,12 +6510,12 @@ check_upstream_config(PGconn *conn, int server_version_num, t_node_info *upstrea
|
||||
if (mode == pg_basebackup)
|
||||
{
|
||||
|
||||
PGconn **connections;
|
||||
int i;
|
||||
int available_wal_senders;
|
||||
int min_replication_connections = 1;
|
||||
int possible_replication_connections = 0;
|
||||
t_conninfo_param_list repl_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
|
||||
PGconn **connections;
|
||||
int available_wal_senders;
|
||||
int min_replication_connections = 1;
|
||||
int possible_replication_connections = 0;
|
||||
i = 0;
|
||||
t_conninfo_param_list repl_conninfo = T_CONNINFO_PARAM_LIST_INITIALIZER;
|
||||
|
||||
|
||||
/*
|
||||
@@ -7770,6 +7785,117 @@ stop_backup:
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Perform a call to pg_backupapi endpoint to ask barman to write the backup
|
||||
* for us. This will ensure that no matter the format on-disk of new backups,
|
||||
* barman will always find a way how to read and write them.
|
||||
* From repmgr 4 this is only used for Barman backups.
|
||||
*/
|
||||
static int
|
||||
run_pg_backupapi(t_node_info *local_node_record)
|
||||
{
|
||||
int r = ERR_PGBACKUPAPI_SERVICE;
|
||||
long http_return_code = 0;
|
||||
short seconds_to_sleep = 3;
|
||||
operation_task *task = malloc(sizeof(operation_task));
|
||||
CURL *curl = curl_easy_init();
|
||||
CURLcode ret;
|
||||
|
||||
check_pg_backupapi_standby_clone_options();
|
||||
|
||||
task->host = malloc(strlen(config_file_options.pg_backupapi_host)+1);
|
||||
task->remote_ssh_command = malloc(strlen(config_file_options.pg_backupapi_remote_ssh_command)+1);
|
||||
task->node_name = malloc(strlen(config_file_options.pg_backupapi_node_name)+1);
|
||||
task->operation_type = malloc(strlen(DEFAULT_STANDBY_PG_BACKUPAPI_OP_TYPE)+1);
|
||||
task->backup_id = malloc(strlen(config_file_options.pg_backupapi_backup_id)+1);
|
||||
task->destination_directory = malloc(strlen(local_data_directory)+1);
|
||||
|
||||
task->operation_id = malloc(MAX_BUFFER_LENGTH);
|
||||
task->operation_status = malloc(MAX_BUFFER_LENGTH);
|
||||
|
||||
strcpy(task->host, config_file_options.pg_backupapi_host);
|
||||
strcpy(task->remote_ssh_command, config_file_options.pg_backupapi_remote_ssh_command);
|
||||
strcpy(task->node_name, config_file_options.pg_backupapi_node_name);
|
||||
strcpy(task->operation_type, DEFAULT_STANDBY_PG_BACKUPAPI_OP_TYPE);
|
||||
strcpy(task->backup_id, config_file_options.pg_backupapi_backup_id);
|
||||
strcpy(task->destination_directory, local_data_directory);
|
||||
strcpy(task->operation_id, "\0");
|
||||
|
||||
ret = create_new_task(curl, task);
|
||||
|
||||
if ((ret != CURLE_OK) || (strlen(task->operation_id) == 0)) {
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_return_code);
|
||||
if (499 > http_return_code && http_return_code >= 400) {
|
||||
log_error("Cannot find backup '%s' for node '%s'.", task->backup_id, task->node_name);
|
||||
} else {
|
||||
log_error("whilst reaching out pg_backup service: %s\n", curl_easy_strerror(ret));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_info("Success creating the task: operation id '%s'", task->operation_id);
|
||||
|
||||
//We call init again because previous call included POST calls
|
||||
curl_easy_cleanup(curl);
|
||||
curl = curl_easy_init();
|
||||
while (true)
|
||||
{
|
||||
ret = get_status_of_operation(curl, task);
|
||||
if (strlen(task->operation_status) == 0) {
|
||||
log_info("Retrying...");
|
||||
}
|
||||
else
|
||||
{
|
||||
log_info("status %s", task->operation_status);
|
||||
}
|
||||
if (strcmp(task->operation_status, "FAILED") == 0) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(task->operation_status, "DONE") == 0) {
|
||||
r = SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(seconds_to_sleep);
|
||||
}
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
free(task);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_backupapi mode is enabled when config_file_options.pg_backupapi_host is set hence, we
|
||||
* should also check the other required variables too.
|
||||
*/
|
||||
|
||||
void check_pg_backupapi_standby_clone_options() {
|
||||
|
||||
bool error = false;
|
||||
|
||||
if (*config_file_options.pg_backupapi_remote_ssh_command == '\0') {
|
||||
log_hint("Check config: remote ssh command is required");
|
||||
error = true;
|
||||
}
|
||||
if (*config_file_options.pg_backupapi_node_name == '\0') {
|
||||
log_hint("Check config: node name is required");
|
||||
error = true;
|
||||
}
|
||||
if (*config_file_options.pg_backupapi_backup_id == '\0') {
|
||||
log_hint("Check config: backup_id is required");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error == true) {
|
||||
log_error("Please fix the errors and try again");
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *
|
||||
make_barman_ssh_command(char *buf)
|
||||
{
|
||||
@@ -8351,7 +8477,6 @@ check_sibling_nodes(NodeInfoList *sibling_nodes, SiblingNodeStats *sibling_nodes
|
||||
if (sibling_nodes->node_count > 0)
|
||||
{
|
||||
PQExpBufferData nodes;
|
||||
NodeInfoListCell *cell;
|
||||
|
||||
initPQExpBuffer(&nodes);
|
||||
|
||||
|
||||
@@ -30,6 +30,6 @@ extern void do_standby_help(void);
|
||||
|
||||
extern bool do_standby_follow_internal(PGconn *primary_conn, PGconn *follow_target_conn, t_node_info *follow_target_node_record, PQExpBufferData *output, int general_error_code, int *error_code);
|
||||
|
||||
|
||||
void check_pg_backupapi_standby_clone_options(void);
|
||||
|
||||
#endif /* _REPMGR_ACTION_STANDBY_H_ */
|
||||
|
||||
@@ -193,7 +193,8 @@ typedef struct
|
||||
typedef enum
|
||||
{
|
||||
barman,
|
||||
pg_basebackup
|
||||
pg_basebackup,
|
||||
pg_backupapi
|
||||
} standy_clone_mode;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -70,7 +70,11 @@
|
||||
#include "repmgr-action-service.h"
|
||||
#include "repmgr-action-daemon.h"
|
||||
|
||||
#if (PG_VERSION_NUM >= 170000)
|
||||
#include <common/file_utils.h> /* for PG_TEMP_FILE_PREFIX */
|
||||
#else
|
||||
#include <storage/fd.h> /* for PG_TEMP_FILE_PREFIX */
|
||||
#endif
|
||||
|
||||
/* globally available variables *
|
||||
* ============================ */
|
||||
@@ -3096,9 +3100,14 @@ get_standby_clone_mode(void)
|
||||
|
||||
if (*config_file_options.barman_host != '\0' && runtime_options.without_barman == false)
|
||||
mode = barman;
|
||||
else
|
||||
mode = pg_basebackup;
|
||||
|
||||
else {
|
||||
if (*config_file_options.pg_backupapi_host != '\0') {
|
||||
log_info("Attempting to use `pg_backupapi` new restore mode");
|
||||
mode = pg_backupapi;
|
||||
}
|
||||
else
|
||||
mode = pg_basebackup;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -3634,8 +3643,8 @@ can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *rea
|
||||
/* "full_page_writes" must be on in any case */
|
||||
if (guc_set(conn, "full_page_writes", "=", "off"))
|
||||
{
|
||||
appendPQExpBuffer(reason,
|
||||
_("\"full_page_writes\" must be set to \"on\""));
|
||||
appendPQExpBufferStr(reason,
|
||||
_("\"full_page_writes\" must be set to \"on\""));
|
||||
|
||||
can_use = false;
|
||||
}
|
||||
@@ -3653,18 +3662,18 @@ can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBufferData *rea
|
||||
if (data_checksum_version == UNKNOWN_DATA_CHECKSUM_VERSION)
|
||||
{
|
||||
if (can_use == false)
|
||||
appendPQExpBuffer(reason, "; ");
|
||||
appendPQExpBufferStr(reason, "; ");
|
||||
|
||||
appendPQExpBuffer(reason,
|
||||
_("\"wal_log_hints\" is set to \"off\" but unable to determine data checksum version"));
|
||||
appendPQExpBufferStr(reason,
|
||||
_("\"wal_log_hints\" is set to \"off\" but unable to determine data checksum version"));
|
||||
can_use = false;
|
||||
}
|
||||
else if (data_checksum_version == 0)
|
||||
{
|
||||
if (can_use == false)
|
||||
appendPQExpBuffer(reason, "; ");
|
||||
appendPQExpBufferStr(reason, "; ");
|
||||
|
||||
appendPQExpBuffer(reason,
|
||||
appendPQExpBufferStr(reason,
|
||||
_("\"wal_log_hints\" is set to \"off\" and data checksums are disabled"));
|
||||
|
||||
can_use = false;
|
||||
@@ -3873,19 +3882,17 @@ drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name)
|
||||
*/
|
||||
|
||||
bool use_replication_protocol = false;
|
||||
PGconn *slot_conn = NULL;
|
||||
|
||||
slot_conn = _get_replication_slot_connection(conn,
|
||||
replication_user,
|
||||
&use_replication_protocol);
|
||||
PGconn *slot_conn = _get_replication_slot_connection(conn,
|
||||
replication_user,
|
||||
&use_replication_protocol);
|
||||
|
||||
if (use_replication_protocol == true)
|
||||
{
|
||||
success = drop_replication_slot_replprot(conn, slot_name);
|
||||
success = drop_replication_slot_replprot(slot_conn, slot_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = drop_replication_slot_sql(conn, slot_name);
|
||||
success = drop_replication_slot_sql(slot_conn, slot_name);
|
||||
}
|
||||
|
||||
if (success == true)
|
||||
@@ -3965,9 +3972,13 @@ _determine_replication_slot_user(PGconn *conn, t_node_info *upstream_node_record
|
||||
/* This should never happen */
|
||||
log_error("unable to determine replication slot user");
|
||||
if (upstream_node_record != NULL)
|
||||
{
|
||||
log_debug("%i %s %s", upstream_node_record->node_id, upstream_node_record->repluser, PQuser(conn));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debug("upstream_node_record not provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3984,13 +3995,13 @@ _get_replication_slot_connection(PGconn *conn, char *replication_user, bool *use
|
||||
switch (ReplicationSlotUser)
|
||||
{
|
||||
case USER_TYPE_UNKNOWN:
|
||||
log_error("unable to determine user for replication slot creation");
|
||||
log_error("unable to determine user for managing replication slots");
|
||||
return NULL;
|
||||
|
||||
case REPMGR_USER:
|
||||
slot_conn = conn;
|
||||
log_info(_("creating replication slot as user \"%s\""),
|
||||
PQuser(conn));
|
||||
log_verbose(LOG_INFO, _("managing replication slot as user \"%s\""),
|
||||
PQuser(conn));
|
||||
break;
|
||||
|
||||
case REPLICATION_USER_NODE:
|
||||
@@ -4001,7 +4012,7 @@ _get_replication_slot_connection(PGconn *conn, char *replication_user, bool *use
|
||||
true);
|
||||
if (slot_conn == NULL || PQstatus(slot_conn) != CONNECTION_OK)
|
||||
{
|
||||
log_error(_("unable to create replication connection as user \"%s\""),
|
||||
log_error(_("unable to manage replication connection as replication user \"%s\""),
|
||||
runtime_options.replication_user);
|
||||
log_detail("%s", PQerrorMessage(slot_conn));
|
||||
|
||||
@@ -4009,8 +4020,8 @@ _get_replication_slot_connection(PGconn *conn, char *replication_user, bool *use
|
||||
return NULL;
|
||||
}
|
||||
*use_replication_protocol = true;
|
||||
log_info(_("creating replication slot as replication user \"%s\""),
|
||||
replication_user);
|
||||
log_verbose(LOG_INFO, _("managing replication slot as replication user \"%s\""),
|
||||
replication_user);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -4021,7 +4032,7 @@ _get_replication_slot_connection(PGconn *conn, char *replication_user, bool *use
|
||||
false);
|
||||
if (slot_conn == NULL || PQstatus(slot_conn )!= CONNECTION_OK)
|
||||
{
|
||||
log_error(_("unable to create super connection as user \"%s\""),
|
||||
log_error(_("unable to create superuser connection as user \"%s\""),
|
||||
runtime_options.superuser);
|
||||
log_detail("%s", PQerrorMessage(slot_conn));
|
||||
|
||||
@@ -4029,8 +4040,8 @@ _get_replication_slot_connection(PGconn *conn, char *replication_user, bool *use
|
||||
|
||||
return NULL;
|
||||
}
|
||||
log_info(_("creating replication slot as superuser \"%s\""),
|
||||
runtime_options.superuser);
|
||||
log_verbose(LOG_INFO, _("creating replication slot as superuser \"%s\""),
|
||||
runtime_options.superuser);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
18
repmgr.c
18
repmgr.c
@@ -86,7 +86,6 @@ static shmem_request_hook_type prev_shmem_request_hook = NULL;
|
||||
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
|
||||
|
||||
void _PG_init(void);
|
||||
void _PG_fini(void);
|
||||
|
||||
#if (PG_VERSION_NUM >= 150000)
|
||||
static void repmgr_shmem_request(void);
|
||||
@@ -145,21 +144,6 @@ _PG_init(void)
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module unload callback
|
||||
*/
|
||||
void
|
||||
_PG_fini(void)
|
||||
{
|
||||
/* Uninstall hook */
|
||||
#if (PG_VERSION_NUM >= 150000)
|
||||
shmem_request_hook = prev_shmem_request_hook;
|
||||
#endif
|
||||
|
||||
shmem_startup_hook = prev_shmem_startup_hook;
|
||||
}
|
||||
|
||||
#if (PG_VERSION_NUM >= 150000)
|
||||
/*
|
||||
* shmem_requst_hook: request shared memory
|
||||
@@ -177,7 +161,7 @@ repmgr_shmem_request(void)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* shmem_ hook: allocate or attach to shared memory,
|
||||
* shmem_startup hook: allocate or attach to shared memory
|
||||
*/
|
||||
static void
|
||||
repmgr_shmem_startup(void)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#
|
||||
# For details on the configuration file format see the documentation at:
|
||||
#
|
||||
# https://repmgr.org/docs/current/configuration-file.html#CONFIGURATION-FILE-FORMAT
|
||||
# https://repmgr.org/docs/current/configuration-file.html#CONFIGURATION-FILE-FORMAT
|
||||
#
|
||||
# =============================================================================
|
||||
# Required configuration items
|
||||
@@ -76,7 +76,7 @@
|
||||
#location='default' # An arbitrary string defining the location of the node; this
|
||||
# is used during failover to check visibility of the
|
||||
# current primary node. For further details see:
|
||||
# https://repmgr.org/docs/current/repmgrd-network-split.html
|
||||
# https://repmgr.org/docs/current/repmgrd-network-split.html
|
||||
|
||||
#use_replication_slots=no # whether to use physical replication slots
|
||||
# NOTE: when using replication slots,
|
||||
@@ -181,8 +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"
|
||||
# (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"
|
||||
|
||||
@@ -212,8 +212,8 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
|
||||
#recovery_min_apply_delay= # If provided, "recovery_min_apply_delay" will be set to
|
||||
# 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').
|
||||
# an integer representing milliseconds, or a string
|
||||
# representing a period of time (e.g. '5 min').
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -299,7 +299,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# a value of zero prevents the node being promoted to primary
|
||||
# (default: 100)
|
||||
|
||||
#connection_check_type=ping # How to check availability of the upstream node; valid options:
|
||||
#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': attempt to make a new connection to the node
|
||||
# 'query': execute an SQL statement on the node via the existing connection
|
||||
@@ -340,22 +340,31 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
#repmgrd_exit_on_inactive_node=false # If "true", and the node record is marked as "inactive", abort repmgrd startup
|
||||
#standby_disconnect_on_failover=false # If "true", in a failover situation wait for all standbys to
|
||||
# disconnect their WAL receivers before electing a new primary
|
||||
# (PostgreSQL 9.5 and later only; repmgr user must be a superuser for this)
|
||||
# Can be true in PostgreSQL 9.5 and later only. Until PostgreSQL 14 repmgr user must be a superuser to use this.
|
||||
# From PostgreSQL 15 repmgr must be a superuser or have 'ALTER SYSTEM wal_retrieve_retry_interval' privilege.
|
||||
# (see: https://repmgr.org/docs/current/repmgrd-standby-disconnection-on-failover.html )
|
||||
#sibling_nodes_disconnect_timeout=30 # If "standby_disconnect_on_failover" is true, the maximum length of time
|
||||
# (in seconds) to wait for other standbys to confirm they have disconnected their
|
||||
# (in seconds) to wait for other standbys to confirm they have disconnected their
|
||||
# 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
|
||||
# value: %n (node_id), %a (node_name). *Must* be the same on all nodes.
|
||||
#failover_validation_command='' # Script to execute for an external mechanism to validate the failover
|
||||
# decision made by repmgrd. Each of the following parameter placeholders
|
||||
# should be provided, which will be replaced by repmgrd with the appropriate value:
|
||||
# %n (node_id)
|
||||
# %a (node_name)
|
||||
# %v (number of visible nodes)
|
||||
# %u (number of shared upstream nodes)
|
||||
# %t (total number of nodes)
|
||||
# *Must* be the same on all nodes.
|
||||
#election_rerun_interval=15 # if "failover_validation_command" is set, and the command returns
|
||||
# an error, pause the specified amount of seconds before rerunning the election.
|
||||
#
|
||||
# The following items are relevant for repmgrd running on the primary,
|
||||
# and will be ignored on non-primary nodes
|
||||
|
||||
# The following items are relevant for repmgrd running on the primary,
|
||||
# and will be ignored on non-primary nodes.
|
||||
# (see: https://repmgr.org/docs/current/repmgrd-primary-child-disconnection.html )
|
||||
|
||||
#child_nodes_check_interval=5 # Interval (in seconds) to check for attached child nodes (standbys)
|
||||
#child_nodes_connected_min_count=-1 # Minimum number of child nodes which must remain connected, otherwise
|
||||
# disconnection command will be triggered
|
||||
@@ -363,6 +372,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# (ignored if "child_nodes_connected_min_count" set)
|
||||
#child_nodes_disconnect_timeout=30 # Interval between child node disconnection and disconnection command execution
|
||||
#child_nodes_disconnect_command='' # Command to execute if child node disconnection detected
|
||||
#child_nodes_connected_include_witness=false # Whether to count the witness node (if in use) as a child node when determining whether to execute child_nodes_disconnect_command.
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# service control commands
|
||||
@@ -385,20 +395,20 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
#
|
||||
# For example, to use systemd, you can set
|
||||
#
|
||||
# service_start_command = 'sudo systemctl start postgresql-9.6'
|
||||
# service_start_command = 'sudo systemctl start postgresql-16'
|
||||
# (...)
|
||||
#
|
||||
# and then use the following sudoers configuration:
|
||||
#
|
||||
# # this is required when running sudo over ssh without -t:
|
||||
# Defaults:postgres !requiretty
|
||||
# postgres ALL = NOPASSWD: /usr/bin/systemctl stop postgresql-9.6, \
|
||||
# /usr/bin/systemctl start postgresql-9.6, \
|
||||
# /usr/bin/systemctl restart postgresql-9.6
|
||||
# postgres ALL = NOPASSWD: /usr/bin/systemctl stop postgresql-16, \
|
||||
# /usr/bin/systemctl start postgresql-16, \
|
||||
# /usr/bin/systemctl restart postgresql-16
|
||||
#
|
||||
# Debian/Ubuntu users: use "sudo pg_ctlcluster" to execute service control commands.
|
||||
#
|
||||
# For more details, see: https://repmgr.org/docs/current/configuration-file-service-commands.html
|
||||
# For further details, see: https://repmgr.org/docs/current/configuration-file-service-commands.html
|
||||
|
||||
#service_start_command = ''
|
||||
#service_stop_command = ''
|
||||
@@ -441,4 +451,3 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
|
||||
# "repmgr standby switchover" to warn about potential
|
||||
# issues with shutting down the demotion candidate.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# repmgr extension
|
||||
comment = 'Replication manager for PostgreSQL'
|
||||
default_version = '5.4'
|
||||
default_version = '5.5'
|
||||
module_pathname = '$libdir/repmgr'
|
||||
relocatable = false
|
||||
schema = repmgr
|
||||
|
||||
1
repmgr.h
1
repmgr.h
@@ -116,6 +116,7 @@
|
||||
#define DEFAULT_STANDBY_FOLLOW_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_STANDBY_FOLLOW_RESTART false
|
||||
#define DEFAULT_SHUTDOWN_CHECK_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_STANDBY_PG_BACKUPAPI_OP_TYPE "recovery"
|
||||
#define DEFAULT_STANDBY_RECONNECT_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_NODE_REJOIN_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_ARCHIVE_READY_WARNING 16 /* WAL files */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#define REPMGR_VERSION_DATE ""
|
||||
#define REPMGR_VERSION "5.4dev"
|
||||
#define REPMGR_VERSION_NUM 50400
|
||||
#define REPMGR_EXTENSION_VERSION "5.4"
|
||||
#define REPMGR_EXTENSION_NUM 50400
|
||||
#define REPMGR_RELEASE_DATE "2022-XX-XX"
|
||||
#define REPMGR_VERSION_DATE "2024-11-20"
|
||||
#define REPMGR_VERSION "5.5.0"
|
||||
#define REPMGR_VERSION_NUM 50500
|
||||
#define REPMGR_EXTENSION_VERSION "5.5.0"
|
||||
#define REPMGR_EXTENSION_NUM 50500
|
||||
#define REPMGR_RELEASE_DATE "2024-XX-XX"
|
||||
#define PG_ACTUAL_VERSION_NUM
|
||||
|
||||
@@ -1359,6 +1359,8 @@ monitor_streaming_standby(void)
|
||||
MonitoringState local_monitoring_state = MS_NORMAL;
|
||||
instr_time local_degraded_monitoring_start;
|
||||
|
||||
bool upstream_check_result;
|
||||
int degraded_monitoring_elapsed;
|
||||
int last_known_upstream_node_id = UNKNOWN_NODE_ID;
|
||||
|
||||
log_debug("monitor_streaming_standby()");
|
||||
@@ -1547,7 +1549,6 @@ monitor_streaming_standby(void)
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool upstream_check_result;
|
||||
|
||||
log_verbose(LOG_DEBUG, "checking %s", upstream_node_info.conninfo);
|
||||
|
||||
@@ -1587,22 +1588,22 @@ monitor_streaming_standby(void)
|
||||
/* TODO: possibly add pre-action event here */
|
||||
if (upstream_node_info.type == STANDBY)
|
||||
{
|
||||
create_event_record(primary_conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_upstream_disconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
create_event_notification(primary_conn,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_upstream_disconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* primary connection lost - script notification only */
|
||||
create_event_record(NULL,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_upstream_disconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
create_event_notification(NULL,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_upstream_disconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
}
|
||||
|
||||
log_warning("%s", event_details.data);
|
||||
@@ -1770,8 +1771,7 @@ monitor_streaming_standby(void)
|
||||
|
||||
if (monitoring_state == MS_DEGRADED)
|
||||
{
|
||||
int degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
|
||||
bool upstream_check_result;
|
||||
degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
|
||||
|
||||
if (config_file_options.degraded_monitoring_timeout > 0
|
||||
&& degraded_monitoring_elapsed > config_file_options.degraded_monitoring_timeout)
|
||||
@@ -1894,7 +1894,6 @@ monitor_streaming_standby(void)
|
||||
*/
|
||||
if (failover_state == FAILOVER_STATE_PROMOTION_FAILED)
|
||||
{
|
||||
int degraded_monitoring_elapsed;
|
||||
int former_upstream_node_id = local_node_info.upstream_node_id;
|
||||
NodeInfoList sibling_nodes = T_NODE_INFO_LIST_INITIALIZER;
|
||||
PQExpBufferData event_details;
|
||||
@@ -1953,7 +1952,7 @@ monitor_streaming_standby(void)
|
||||
|
||||
if (local_node_info.type == PRIMARY)
|
||||
{
|
||||
int degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
|
||||
degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
|
||||
|
||||
log_notice(_("resuming monitoring as primary node after %i seconds"),
|
||||
degraded_monitoring_elapsed);
|
||||
@@ -2394,6 +2393,17 @@ monitor_streaming_witness(void)
|
||||
terminate(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that the primary changed while the witness repmgrd was not
|
||||
* running. This does not affect the functionality of the witness repmgrd, but
|
||||
* does mean outdated node metadata will be displayed, so update that.
|
||||
*/
|
||||
if (local_node_info.upstream_node_id != primary_node_id)
|
||||
{
|
||||
update_node_record_set_upstream(primary_conn, local_node_info.node_id, primary_node_id);
|
||||
local_node_info.upstream_node_id = primary_node_id;
|
||||
}
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
@@ -2482,12 +2492,12 @@ monitor_streaming_witness(void)
|
||||
_("unable to connect to primary node \"%s\" (ID: %i)"),
|
||||
upstream_node_info.node_name, upstream_node_info.node_id);
|
||||
|
||||
create_event_record(NULL,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_upstream_disconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
create_event_notification(NULL,
|
||||
&config_file_options,
|
||||
config_file_options.node_id,
|
||||
"repmgrd_upstream_disconnect",
|
||||
true,
|
||||
event_details.data);
|
||||
termPQExpBuffer(&event_details);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user