Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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
13 to 18. 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.
Expand Down
20 changes: 20 additions & 0 deletions configdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,26 @@ struct ConfigFileSetting config_file_settings[] =
{ .strmaxlen = sizeof(config_file_options.pg_backupapi_remote_ssh_command) },
{}
},
/* pgbackrest_config_file */
{
"pgbackrest_config_file",
CONFIG_STRING,
{ .strptr = config_file_options.pgbackrest_config_file },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.pgbackrest_config_file) },
{ .postprocess_func = &repmgr_canonicalize_path }
},
/* pgbackrest_stanza */
{
"pgbackrest_stanza",
CONFIG_STRING,
{ .strptr = config_file_options.pgbackrest_stanza },
{ .strdefault = "" },
{},
{ .strmaxlen = sizeof(config_file_options.pgbackrest_stanza) },
{}
},

/* =======================
* standby follow settings
Expand Down
2 changes: 2 additions & 0 deletions configfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ typedef struct
char pg_backupapi_host[NAMEDATALEN];
char pg_backupapi_node_name[NAMEDATALEN];
char pg_backupapi_remote_ssh_command[MAXLEN];
char pgbackrest_config_file[MAXPGPATH];
char pgbackrest_stanza[NAMEDATALEN];

/* standby promote settings */
int promote_check_timeout;
Expand Down
93 changes: 93 additions & 0 deletions doc/cloning-standbys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,99 @@ HINT: after starting the server, you need to register this standby with "repmgr
</sect2> <!--END cloning-from-barman-pg_backupapi-mode !-->
</sect1>

<sect1 id="cloning-from-pgbackrest" xreflabel="Cloning from pgBackRest">
<title>Cloning a standby from pgBackRest</title>

<indexterm>
<primary>cloning</primary>
<secondary>from pgBackRest</secondary>
</indexterm>
<indexterm>
<primary>pgBackRest</primary>
<secondary>cloning a standby</secondary>
</indexterm>

<para>
<xref linkend="repmgr-standby-clone"/> can use
<ulink url="https://pgbackrest.org/">pgBackRest</ulink>
to clone a standby.
</para>

<para>
pgBackRest support provides the following advantages:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<para>
the primary node does not need to perform a new backup every time a
new standby is cloned
</para>
</listitem>
<listitem>
<para>
a standby node can be disconnected for longer periods without losing
the ability to catch up, and without causing accumulation of WAL
files on the primary node
</para>
</listitem>
</itemizedlist>
</para>

<sect2 id="cloning-from-pgbackrest-prerequisites">
<title>Prerequisites for cloning from pgBackRest</title>
<para>
In order to enable pgBackRest support for <command>repmgr standby clone</command>, following
prerequisites must be met:
<itemizedlist spacing="compact" mark="bullet">
<listitem>
<para>
the pgBackRest archive must include at least one valid backup for this server;
</para>
</listitem>
<listitem>
<para>
the <varname>pgbackrest_config_file</varname> setting in <filename>repmgr.conf</filename> is set to the full path
to the pgBackRest configuration file;
</para>
</listitem>
<listitem>
<para>
the <varname>pgbackrest_stanza</varname> setting in <filename>repmgr.conf</filename> is set to the name of the
stanza defined in that configuration file.
</para>
</listitem>
</itemizedlist>
</para>

<para>
For example, <filename>repmgr.conf</filename> should contain the following entries:
<programlisting>
pgbackrest_config_file='/etc/pgbackrest/pgbackrest.conf'
pgbackrest_stanza='demo'</programlisting>
</para>

<para>
Note that <command>repmgr standby clone</command> will execute the <command>pgbackrest restore</command>
command using the options defined in the pgBackRest configuration file, without adding
additional options to override them.
</para>

<para>
To use pgBackRest as a WAL file source, <varname>restore_command</varname> in <filename>repmgr.conf</filename>
should be configured to use the <command>pgbackrest archive-get</command> command, e.g.:
<programlisting>
restore_command='pgbackrest --stanza=demo archive-get %f %p'</programlisting>
</para>

<note>
<simpara>
pgBackRest support is automatically enabled if <varname>pgbackrest_stanza</varname>
is set. pgBackRest mode can be disabled using the <literal>--without-pgbackrest</literal>
command line option.
</simpara>
</note>
</sect2>
</sect1>

<sect1 id="cloning-replication-slots" xreflabel="Cloning and replication slots">
<title>Cloning and replication slots</title>

Expand Down
21 changes: 16 additions & 5 deletions doc/repmgr-standby-clone.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
<para>
<command>repmgr standby clone</command> clones a PostgreSQL node from another
PostgreSQL node, typically the primary, but optionally from any other node in
the cluster or from Barman. It creates the replication configuration required
to attach the cloned node to the primary node (or another standby, if cascading replication
is in use).
the cluster or from a backup tool like Barman or pgBackRest.
It creates the replication configuration required to attach the cloned node
to the primary node (or another standby, if cascading replication is in use).
</para>
<note>
<simpara>
Expand Down Expand Up @@ -234,7 +234,10 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>

<para>
&repmgr; supports standbys cloned by another method (e.g. using <application>barman</application>'s
<command><ulink url="https://docs.pgbarman.org/#recover">barman recover</ulink></command> command).
<command><ulink url="https://docs.pgbarman.org/#recover">barman recover</ulink></command> command,
or using <application>pgBackRest</application>'s
<command><ulink url="https://pgbackrest.org/command.html#command-restore">pgbackrest restore</ulink></command>
command).
</para>
<para>
To integrate the standby as a &repmgr; node, once the standby has been cloned,
Expand Down Expand Up @@ -467,6 +470,15 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
</listitem>
</varlistentry>

<varlistentry>
<term><option>--without-pgbackrest</option></term>
<listitem>
<para>
Do not use pgBackRest even if configured.
</para>
</listitem>
</varlistentry>

</variablelist>
</refsect1>

Expand All @@ -484,4 +496,3 @@ pg_basebackup_options='--waldir=/path/to/wal-directory'</programlisting>
</para>
</refsect1>
</refentry>

2 changes: 1 addition & 1 deletion doc/repmgr.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<abstract>
<para>
This is the official documentation of &repmgr; &repmgrversion; for
use with PostgreSQL 12 - PostgreSQL 17.
use with PostgreSQL 12 - PostgreSQL 18.
</para>
<para>
&repmgr; is being continually developed and we strongly recommend using the
Expand Down
156 changes: 156 additions & 0 deletions repmgr-action-standby.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,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_pgbackrest(t_node_info *local_node_record);
static int run_pg_backupapi(t_node_info *node_record);

static void copy_configuration_files(bool delete_after_copy);
Expand Down Expand Up @@ -701,6 +702,9 @@ do_standby_clone(void)
case pg_backupapi:
log_notice(_("starting backup (using pg_backupapi)..."));
break;
case pgbackrest:
log_notice(_("starting backup (using pgBackRest)..."));
break;
default:
/* should never reach here */
log_error(_("unknown clone mode"));
Expand All @@ -725,6 +729,9 @@ do_standby_clone(void)
case pg_backupapi:
r = run_pg_backupapi(&local_node_record);
break;
case pgbackrest:
r = run_pgbackrest(&local_node_record);
break;
default:
/* should never reach here */
log_error(_("unknown clone mode"));
Expand Down Expand Up @@ -946,6 +953,9 @@ do_standby_clone(void)
case pg_backupapi:
appendPQExpBufferStr(&event_details, "pg_backupapi");
break;
case pgbackrest:
appendPQExpBufferStr(&event_details, "pgbackrest");
break;
}

appendPQExpBuffer(&event_details,
Expand Down Expand Up @@ -7784,6 +7794,151 @@ run_file_backup(t_node_info *local_node_record)
return r;
}

static int
run_pgbackrest(t_node_info *local_node_record)
{
PQExpBufferData command;
int r = SUCCESS;
RecordStatus record_status = RECORD_NOT_FOUND;

/* Ensure data directory is ready */
if (runtime_options.dry_run == false)
{
if (!create_pg_dir(local_data_directory, runtime_options.force))
{
log_error(_("unable to use directory \"%s\""), local_data_directory);
log_hint(_("use -F/--force to force this directory to be overwritten"));
return ERR_BAD_CONFIG;
}
}

initPQExpBuffer(&command);

appendPQExpBufferStr(&command, "pgbackrest");

if (*config_file_options.pgbackrest_config_file != '\0')
{
appendPQExpBuffer(&command, " --config=%s", config_file_options.pgbackrest_config_file);
}

appendPQExpBuffer(&command, " --stanza=%s", config_file_options.pgbackrest_stanza);

appendPQExpBufferStr(&command, " restore");

if (runtime_options.dry_run == true)
{
log_info(_("would execute:\n %s"), command.data);
termPQExpBuffer(&command);
return SUCCESS;
}

log_info(_("executing:\n %s"), command.data);

if (local_command(command.data, NULL) == false)
{
r = ERR_BAD_BASEBACKUP;
}

termPQExpBuffer(&command);

/* If restore failed, return early */
if (r != SUCCESS)
return r;

/*
* if replication slots in use, create replication slot
*/
if (config_file_options.use_replication_slots == true)
{
bool slot_warning = false;

if (runtime_options.no_upstream_connection == true)
{
slot_warning = true;
}
else
{
t_node_info upstream_node_record = T_NODE_INFO_INITIALIZER;
t_replication_slot slot_info = T_REPLICATION_SLOT_INITIALIZER;
PGconn *upstream_conn = NULL;

/* check connections are still available */
(void) connection_ping_reconnect(primary_conn);

if (source_conn != primary_conn)
(void) connection_ping_reconnect(source_conn);

record_status = get_node_record(source_conn, upstream_node_id, &upstream_node_record);

if (record_status != RECORD_FOUND)
{
log_error(_("unable to retrieve node record for upstream node %i"), upstream_node_id);
slot_warning = true;
}
else
{
upstream_conn = establish_db_connection(upstream_node_record.conninfo, false);
if (PQstatus(upstream_conn) != CONNECTION_OK)
{
log_error(_("unable to connect to upstream node %i to create a replication slot"), upstream_node_id);
slot_warning = true;
}
else
{
record_status = get_slot_record(upstream_conn, local_node_record->slot_name, &slot_info);

if (record_status == RECORD_FOUND)
{
log_verbose(LOG_INFO,
_("replication slot \"%s\" already exists on upstream node %i"),
local_node_record->slot_name,
upstream_node_id);
}
else
{
PQExpBufferData errmsg;
bool success;

initPQExpBuffer(&errmsg);
success = create_replication_slot(upstream_conn,
local_node_record->slot_name,
&upstream_node_record,
&errmsg);
if (success == false)
{
log_error(_("unable to create replication slot \"%s\" on upstream node %i"),
local_node_record->slot_name,
upstream_node_id);
log_detail("%s", errmsg.data);
slot_warning = true;
}
else
{
log_notice(_("replication slot \"%s\" created on upstream node \"%s\" (ID: %i)"),
local_node_record->slot_name,
upstream_node_record.node_name,
upstream_node_id);
}
termPQExpBuffer(&errmsg);
}

PQfinish(upstream_conn);
}
}
}

if (slot_warning == true)
{
log_warning(_("\"use_replication_slots\" specified but a replication slot could not be created"));
log_hint(_("ensure a replication slot called \"%s\" is created on the upstream node (ID: %i)"),
local_node_record->slot_name,
upstream_node_id);
}
}

return r;
}


/*
* Perform a call to pg_backupapi endpoint to ask barman to write the backup
Expand Down Expand Up @@ -9243,6 +9398,7 @@ do_standby_help(void)
printf(_(" --verify-backup verify a cloned node using the \"pg_verifybackup\" utility\n"));
#endif
printf(_(" --without-barman do not clone from Barman even if configured\n"));
printf(_(" --without-pgbackrest do not clone from pgBackRest even if configured\n"));
printf(_(" --replication-conf-only generate replication configuration for a previously cloned instance\n"));
printf(_(" --recovery-min-apply-delay set PostgreSQL configuration parameter \"recovery_min_apply_delay\"\n" \
" (overrides any setting in repmgr.conf)\n"));
Expand Down
Loading