Compare commits

..

1 Commits

Author SHA1 Message Date
upm-api-ci
eb06228760 fix(UPM-12035): auto commit from poc-ml 2023-01-05 17:35:04 +00:00
39 changed files with 181 additions and 1057 deletions

7
.github/CODEOWNERS vendored
View File

@@ -1,7 +0,0 @@
# 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 Normal file
View File

@@ -0,0 +1,37 @@
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@v3.3.0
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 Executable file
View File

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

11
HISTORY
View File

@@ -1,14 +1,3 @@
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

View File

@@ -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

View File

@@ -32,9 +32,7 @@ DATA = \
repmgr--5.2--5.3.sql \
repmgr--5.3.sql \
repmgr--5.3--5.4.sql \
repmgr--5.4.sql \
repmgr--5.4--5.5.sql \
repmgr--5.5.sql
repmgr--5.4.sql
REGRESS = repmgr_extension
@@ -68,7 +66,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 pgbackupapi.o
dbutils.o sysutils.o
REPMGRD_OBJS = repmgrd.o repmgrd-physical.o configdata.o configfile.o configfile-scan.o log.o \
dbutils.o strutil.o controldata.o compat.o sysutils.o

View File

@@ -7,9 +7,8 @@ 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.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.
The most recent `repmgr` version (5.3.2) supports all PostgreSQL versions from
9.5 to 14. PostgreSQL 9.4 is also supported, with some restrictions.
`repmgr` is distributed under the GNU GPL 3 and maintained by EnterpriseDB.
@@ -41,6 +40,7 @@ Directories
- `contrib/`: additional utilities
- `doc/`: DocBook-based documentation files
- `expected/`: expected regression test output
- `scripts/`: example scripts
- `sql/`: regression test input
@@ -57,6 +57,8 @@ 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
@@ -69,13 +71,6 @@ 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

View File

@@ -291,46 +291,6 @@ 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

View File

@@ -164,10 +164,6 @@ 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;

View File

@@ -1,6 +1,6 @@
AC_INIT([repmgr], [5.5.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
AC_INIT([repmgr], [5.4.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
AC_COPYRIGHT([Copyright (c) 2010-2024, EnterpriseDB Corporation])
AC_COPYRIGHT([Copyright (c) 2010-2021, EnterpriseDB Corporation])
AC_CONFIG_HEADER(config.h)
@@ -60,7 +60,6 @@ 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
@@ -73,25 +72,6 @@ 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])

106
dbutils.c
View File

@@ -1624,7 +1624,7 @@ get_ready_archive_files(PGconn *conn, const char *data_directory)
while ((arcdir_ent = readdir(arcdir)) != NULL)
{
struct stat local_statbuf;
struct stat 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, &local_statbuf) == 0 && !S_ISREG(local_statbuf.st_mode))
if (stat(file_path, &statbuf) == 0 && !S_ISREG(statbuf.st_mode))
{
continue;
}
@@ -1852,51 +1852,6 @@ 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)
{
@@ -1958,47 +1913,15 @@ can_disable_walsender(PGconn *conn)
if (is_superuser_connection(conn, NULL) == true)
return true;
PQExpBufferData query;
PGresult *res;
bool has_alter_system_priv = false;
/*
* 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"));
/* 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;
return false;
}
/*
@@ -2024,13 +1947,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','USAGE') "
" WHEN pg_catalog.pg_has_role('pg_monitor','MEMBER') "
" THEN TRUE ");
if (subrole != NULL)
{
appendPQExpBuffer(&query,
" WHEN pg_catalog.pg_has_role('%s','USAGE') "
" WHEN pg_catalog.pg_has_role('%s','MEMBER') "
" THEN TRUE ",
subrole);
}
@@ -2537,10 +2460,7 @@ get_repmgr_extension_status(PGconn *conn, t_extension_versions *extversions)
/* node management functions */
/* ========================= */
/*
* Assumes the connection can execute CHECKPOINT command.
* A check can be executed via 'can_execute_checkpoint' function.
*/
/* assumes superuser connection */
void
checkpoint(PGconn *conn)
{

View File

@@ -453,7 +453,6 @@ 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);

View File

@@ -16,59 +16,8 @@
</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>
<title id="release-current">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

View File

@@ -225,109 +225,6 @@ 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">

View File

@@ -10,8 +10,8 @@
<note>
<simpara>
This section documents a subset of optional configuration settings; for a full
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>
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>
</simpara>
</note>

View File

@@ -79,10 +79,6 @@
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>
@@ -152,8 +148,6 @@
<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>,
@@ -165,10 +159,8 @@
<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>. Until PostgreSQL 14 <command>ALTER SYSTEM</command> can only be executed by
<filename>repmgr.conf</filename>. <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>

View File

@@ -14,7 +14,7 @@
</para>
<para>
&repmgr; &repmgrversion; is compatible with all supported PostgreSQL versions from 13.x. See
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 9.4. See
section <link linkend="install-compatibility-matrix">&repmgr; compatibility matrix</link>
for an overview of version compatibility.
</para>
@@ -45,7 +45,7 @@
of incompatibilities.
</simpara>
<simpara>
If different &quot;major&quot; &repmgr; versions (e.g. 5.2.x and &repmgrversion;)
If different &quot;major&quot; &repmgr; versions (e.g. 4.1.x and &repmgrversion;.x)
are installed on different nodes, in the best case &repmgr; (in particular &repmgrd;)
will not run. In the worst case, you will end up with a broken cluster.
</simpara>
@@ -89,6 +89,7 @@
which PostgreSQL version.
</para>
<table id="repmgr-compatibility-matrix">
<title>&repmgr; compatibility matrix</title>
@@ -116,16 +117,16 @@
<tbody>
<row>
<entry>
&repmgr; 5.5
&repmgr; 5.4
</entry>
<entry>
Yes
(dev)
</entry>
<entry>
<link linkend="release-5.5.0">&repmgrversion;</link> (&releasedate;)
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
</entry>
<entry>
12, 13, 15, 16, 17
9.4, 9.5, 9.6, 10, 11, 12, 13, 15
</entry>
<entry>
&nbsp;
@@ -134,31 +135,13 @@
<row>
<entry>
&repmgr; 5.4.1
&repmgr; 5.3
</entry>
<entry>
Yes
YES
</entry>
<entry>
<link linkend="release-5.4.1">5.4.1</link> (2023-04-04)
</entry>
<entry>
10, 11, 12, 13, 15
</entry>
<entry>
&nbsp;
</entry>
</row>
<row>
<entry>
&repmgr; 5.3.1
</entry>
<entry>
Yes
</entry>
<entry>
<link linkend="release-5.3.1">5.3.1</link> (2022-02-15)
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
</entry>
<entry>
9.4, 9.5, 9.6, 10, 11, 12, 13, 14, 15
@@ -173,7 +156,7 @@
&repmgr; 5.2
</entry>
<entry>
No
NO
</entry>
<entry>
<link linkend="release-5.2.1">5.2.1</link> (2020-12-07)
@@ -191,7 +174,7 @@
&repmgr; 5.1
</entry>
<entry>
No
NO
</entry>
<entry>
<link linkend="release-5.1.0">5.1.0</link> (2020-04-13)
@@ -209,7 +192,7 @@
&repmgr; 5.0
</entry>
<entry>
No
NO
</entry>
<entry>
<link linkend="release-5.0">5.0</link> (2019-10-15)
@@ -228,7 +211,7 @@
&repmgr; 4.x
</entry>
<entry>
No
NO
</entry>
<entry>
<link linkend="release-4.4">4.4</link> (2019-06-27)
@@ -246,7 +229,7 @@
&repmgr; 3.x
</entry>
<entry>
No
NO
</entry>
<entry>
<ulink url="https://repmgr.org/release-notes-3.3.2.html">3.3.2</ulink> (2017-05-30)
@@ -264,7 +247,7 @@
&repmgr; 2.x
</entry>
<entry>
No
NO
</entry>
<entry>
<ulink url="https://repmgr.org/release-notes-2.0.3.html">2.0.3</ulink> (2015-04-16)
@@ -283,12 +266,12 @@
<important>
<para>
The &repmgr; series older than 5.x are no longer maintained or supported.
The &repmgr; 2.x and 3.x series are no longer maintained or supported.
We strongly recommend upgrading to the latest &repmgr; version.
</para>
<para>
Following the release of &repmgr; 5.0, there will be no further releases of
the &repmgr; 4.x series or older. Note that &repmgr; 5.x is an incremental development
the &repmgr; 4.x series. Note that &repmgr; 5.x is an incremental development
of the 4.x series and &repmgr; 4.x users should upgrade to this as soon as possible.
</para>
</important>

View File

@@ -1,9 +1,9 @@
<!-- doc/legal.xml -->
<date>2025</date>
<date>2022</date>
<copyright>
<year>2010-2025</year>
<year>2010-2022</year>
<holder>EDB</holder>
</copyright>
@@ -11,7 +11,7 @@
<title>Legal Notice</title>
<para>
<productname>repmgr</productname> is Copyright &copy; 2010-2025
<productname>repmgr</productname> is Copyright &copy; 2010-2022
by EDB All rights reserved.
</para>

View File

@@ -77,8 +77,7 @@
</para>
<para>
Note that a superuser connection is required to be able to execute the
<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.
<command>CHECKPOINT</command> command.
</para>
</listitem>
</varlistentry>

View File

@@ -79,8 +79,7 @@
<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. From PostgreSQL 15 the <varname>pg_checkpoint</varname>
predefined role removes the need for superuser permissions to perform <command>CHECKPOINT</command> command.
provided with the <option>-S</option>/<option>--superuser</option> option.
</para>
<para>
If &repmgr; is unable to execute the <command>CHECKPOINT</command> command, the switchover

View File

@@ -26,7 +26,7 @@
<abstract>
<para>
This is the official documentation of &repmgr; &repmgrversion; for
use with PostgreSQL 12 - PostgreSQL 17.
use with PostgreSQL 9.4 - PostgreSQL 15.
</para>
<para>
&repmgr; is being continually developed and we strongly recommend using the

View File

@@ -279,9 +279,7 @@
<note>
<para>
<option>standby_disconnect_on_failover</option> is available with PostgreSQL 9.5 and later.
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>.
Additionally this requires that the <literal>repmgr</literal> database user is a superuser.
</para>
</note>
<para>

View File

@@ -29,7 +29,7 @@
<listitem>
<simpara>
option to execute custom scripts (&quot;<link linkend="event-notifications">event notifications</link>&quot;)
option to execute custom scripts (&quot;<link linkend="event-notifications">event notifications</link>
at different points in the failover sequence
</simpara>
</listitem>

View File

@@ -49,6 +49,5 @@
#define ERR_NODE_STATUS 25
#define ERR_REPMGRD_PAUSE 26
#define ERR_REPMGRD_SERVICE 27
#define ERR_PGBACKUPAPI_SERVICE 28
#endif /* _ERRCODE_H_ */

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
-- complain if script is sourced in psql, rather than via CREAT EXTENSION
\echo Use "CREATE EXTENSION repmgr" to load this file. \quit

View File

@@ -1,191 +0,0 @@
-- 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
);

View File

@@ -394,6 +394,7 @@ 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);
@@ -523,6 +524,8 @@ 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)
{
@@ -2362,25 +2365,18 @@ do_node_service(void)
conn = establish_db_connection_by_params(&source_conninfo, true);
}
if (can_execute_checkpoint(conn) == false)
if (is_superuser_connection(conn, NULL) == false)
{
if (runtime_options.dry_run == true)
{
log_warning(_("a CHECKPOINT would be issued here but no authorized connection is available"));
log_warning(_("a CHECKPOINT would be issued here but no superuser connection is available"));
}
else
{
log_warning(_("an authorized connection is required to issue a CHECKPOINT"));
log_warning(_("a superuser connection is required to issue a CHECKPOINT"));
}
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"));
}
log_hint(_("provide a superuser with -S/--superuser"));
}
else
{
@@ -2543,7 +2539,6 @@ 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;
@@ -2963,7 +2958,7 @@ do_node_rejoin(void)
else
{
while ((slotdir_ent = readdir(slotdir)) != NULL) {
struct stat local_statbuf;
struct stat statbuf;
PQExpBufferData slotdir_ent_path;
if (strcmp(slotdir_ent->d_name, ".") == 0 || strcmp(slotdir_ent->d_name, "..") == 0)
@@ -2976,7 +2971,7 @@ do_node_rejoin(void)
slotdir_path.data,
slotdir_ent->d_name);
if (stat(slotdir_ent_path.data, &local_statbuf) == 0 && !S_ISDIR(local_statbuf.st_mode))
if (stat(slotdir_ent_path.data, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
{
termPQExpBuffer(&slotdir_ent_path);
continue;
@@ -3107,6 +3102,7 @@ do_node_rejoin(void)
else
{
KeyValueList inactive_replication_slots = {NULL, NULL};
KeyValueListCell *cell = NULL;
int inactive_count = 0;
PQExpBufferData slotinfo;

View File

@@ -235,38 +235,6 @@ 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()
@@ -468,7 +436,7 @@ do_primary_unregister(void)
}
else if (recovery_type == RECTYPE_PRIMARY)
{
reset_node_info(&primary_node_info);
t_node_info primary_node_info = T_NODE_INFO_INITIALIZER;
bool primary_record_found = false;
primary_record_found = get_primary_node_record(primary_conn, &primary_node_info);

View File

@@ -21,7 +21,6 @@
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "repmgr.h"
#include "dirutil.h"
@@ -30,7 +29,7 @@
#include "repmgr-client-global.h"
#include "repmgr-action-standby.h"
#include "pgbackupapi.h"
typedef struct TablespaceDataListCell
{
@@ -114,7 +113,6 @@ 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);
@@ -535,7 +533,7 @@ do_standby_clone(void)
if (external_config_files == true)
{
r = 0;
int r;
PQExpBufferData msg;
initPQExpBuffer(&msg);
@@ -689,18 +687,19 @@ 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"));
@@ -722,9 +721,6 @@ 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"));
@@ -762,7 +758,7 @@ do_standby_clone(void)
if (mode == pg_basebackup && runtime_options.verify_backup == true)
{
PQExpBufferData command;
r = 0;
int r;
struct stat st;
initPQExpBuffer(&command);
@@ -818,6 +814,7 @@ do_standby_clone(void)
}
/* Write the recovery.conf file */
if (create_recovery_file(&local_node_record,
&recovery_conninfo,
source_server_version_num,
@@ -849,9 +846,6 @@ 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;
}
/*
@@ -943,9 +937,6 @@ do_standby_clone(void)
case barman:
appendPQExpBufferStr(&event_details, "barman");
break;
case pg_backupapi:
appendPQExpBufferStr(&event_details, "pg_backupapi");
break;
}
appendPQExpBuffer(&event_details,
@@ -3927,6 +3918,7 @@ 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)
@@ -4783,7 +4775,7 @@ do_standby_switchover(void)
{
NodeInfoListCell *cell = NULL;
ItemList repmgrd_connection_errors = {NULL, NULL};
i = 0;
int i = 0;
int unreachable_node_count = 0;
get_all_node_records(local_conn, &all_nodes);
@@ -5098,6 +5090,8 @@ 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
@@ -5285,7 +5279,7 @@ do_standby_switchover(void)
checkpoint_conn = superuser_conn;
}
if (can_execute_checkpoint(checkpoint_conn) == true)
if (is_superuser_connection(checkpoint_conn, NULL) == true)
{
log_notice(_("issuing CHECKPOINT on node \"%s\" (ID: %i) "),
config_file_options.node_name,
@@ -5294,16 +5288,7 @@ do_standby_switchover(void)
}
else
{
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"));
}
log_warning(_("no superuser connection available, unable to issue CHECKPOINT"));
}
}
@@ -5675,7 +5660,7 @@ do_standby_switchover(void)
{
ItemList repmgrd_unpause_errors = {NULL, NULL};
NodeInfoListCell *cell = NULL;
i = 0;
int i = 0;
int error_node_count = 0;
for (cell = all_nodes.head; cell; cell = cell->next)
@@ -6510,12 +6495,12 @@ check_upstream_config(PGconn *conn, int server_version_num, t_node_info *upstrea
if (mode == pg_basebackup)
{
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;
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;
/*
@@ -7785,117 +7770,6 @@ 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)
{
@@ -8477,6 +8351,7 @@ check_sibling_nodes(NodeInfoList *sibling_nodes, SiblingNodeStats *sibling_nodes
if (sibling_nodes->node_count > 0)
{
PQExpBufferData nodes;
NodeInfoListCell *cell;
initPQExpBuffer(&nodes);

View File

@@ -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_ */

View File

@@ -193,8 +193,7 @@ typedef struct
typedef enum
{
barman,
pg_basebackup,
pg_backupapi
pg_basebackup
} standy_clone_mode;
typedef enum

View File

@@ -70,11 +70,7 @@
#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 *
* ============================ */
@@ -3100,14 +3096,9 @@ get_standby_clone_mode(void)
if (*config_file_options.barman_host != '\0' && runtime_options.without_barman == false)
mode = barman;
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;
}
else
mode = pg_basebackup;
return mode;
}

View File

@@ -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,31 +340,22 @@ 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
# 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 )
# (PostgreSQL 9.5 and later only; repmgr user must be a superuser for this)
#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. 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.
#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.
#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.
# (see: https://repmgr.org/docs/current/repmgrd-primary-child-disconnection.html )
#
# The following items are relevant for repmgrd running on the primary,
# and will be ignored on non-primary nodes
#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
@@ -372,7 +363,6 @@ 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
@@ -395,20 +385,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-16'
# service_start_command = 'sudo systemctl start postgresql-9.6'
# (...)
#
# 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-16, \
# /usr/bin/systemctl start postgresql-16, \
# /usr/bin/systemctl restart postgresql-16
# postgres ALL = NOPASSWD: /usr/bin/systemctl stop postgresql-9.6, \
# /usr/bin/systemctl start postgresql-9.6, \
# /usr/bin/systemctl restart postgresql-9.6
#
# Debian/Ubuntu users: use "sudo pg_ctlcluster" to execute service control commands.
#
# For further details, see: https://repmgr.org/docs/current/configuration-file-service-commands.html
# For more details, see: https://repmgr.org/docs/current/configuration-file-service-commands.html
#service_start_command = ''
#service_stop_command = ''
@@ -451,3 +441,4 @@ 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.

View File

@@ -1,6 +1,6 @@
# repmgr extension
comment = 'Replication manager for PostgreSQL'
default_version = '5.5'
default_version = '5.4'
module_pathname = '$libdir/repmgr'
relocatable = false
schema = repmgr

View File

@@ -116,7 +116,6 @@
#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 */

View File

@@ -1,7 +1,7 @@
#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 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 PG_ACTUAL_VERSION_NUM

View File

@@ -1359,8 +1359,6 @@ 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()");
@@ -1549,6 +1547,7 @@ monitor_streaming_standby(void)
while (true)
{
bool upstream_check_result;
log_verbose(LOG_DEBUG, "checking %s", upstream_node_info.conninfo);
@@ -1771,7 +1770,8 @@ monitor_streaming_standby(void)
if (monitoring_state == MS_DEGRADED)
{
degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
int degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
bool upstream_check_result;
if (config_file_options.degraded_monitoring_timeout > 0
&& degraded_monitoring_elapsed > config_file_options.degraded_monitoring_timeout)
@@ -1894,6 +1894,7 @@ 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;
@@ -1952,7 +1953,7 @@ monitor_streaming_standby(void)
if (local_node_info.type == PRIMARY)
{
degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
int degraded_monitoring_elapsed = calculate_elapsed(degraded_monitoring_start);
log_notice(_("resuming monitoring as primary node after %i seconds"),
degraded_monitoring_elapsed);
@@ -2393,17 +2394,6 @@ 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,