mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Add pg_promote function
This function is able to promote a standby with this new SQL-callable function. Execution access can be granted to non-superusers so that failover tools can observe the principle of least privilege. Catalog version is bumped. Author: Laurenz Albe Reviewed-by: Michael Paquier, Masahiko Sawada Discussion: https://postgr.es/m/6e7c79b3ec916cf49742fb8849ed17cd87aed620.camel@cybertec.at
This commit is contained in:
parent
0a8590b2a0
commit
10074651e3
@ -19202,6 +19202,9 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
|
||||
<indexterm>
|
||||
<primary>pg_is_wal_replay_paused</primary>
|
||||
</indexterm>
|
||||
<indexterm>
|
||||
<primary>pg_promote</primary>
|
||||
</indexterm>
|
||||
<indexterm>
|
||||
<primary>pg_wal_replay_pause</primary>
|
||||
</indexterm>
|
||||
@ -19232,6 +19235,22 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
|
||||
<entry>True if recovery is paused.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal><function>pg_promote(<parameter>wait</parameter> <type>boolean</type> DEFAULT true, <parameter>wait_seconds</parameter> <type>integer</type> DEFAULT 60)</function></literal>
|
||||
</entry>
|
||||
<entry><type>boolean</type></entry>
|
||||
<entry>
|
||||
Promotes a physical standby server. Returns <literal>true</literal>
|
||||
if promotion is successful and <literal>false</literal> otherwise.
|
||||
With <parameter>wait</parameter> set to <literal>true</literal>, the
|
||||
default, the function waits until promotion is completed or
|
||||
<parameter>wait_seconds</parameter> seconds have passed, otherwise the
|
||||
function returns immediately after sending the promotion signal to the
|
||||
postmaster. This function is restricted to superusers by default, but
|
||||
other users can be granted EXECUTE to run the function.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal><function>pg_wal_replay_pause()</function></literal>
|
||||
|
@ -1471,14 +1471,17 @@ synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To trigger failover of a log-shipping standby server,
|
||||
run <command>pg_ctl promote</command> or create a trigger
|
||||
file with the file name and path specified by the <varname>trigger_file</varname>
|
||||
setting in <filename>recovery.conf</filename>. If you're planning to use
|
||||
<command>pg_ctl promote</command> to fail over, <varname>trigger_file</varname> is
|
||||
not required. If you're setting up the reporting servers that are
|
||||
only used to offload read-only queries from the primary, not for high
|
||||
availability purposes, you don't need to promote it.
|
||||
To trigger failover of a log-shipping standby server, run
|
||||
<command>pg_ctl promote</command>, call <function>pg_promote</function>,
|
||||
or create a trigger file with the file name and path specified by the
|
||||
<varname>trigger_file</varname> setting in
|
||||
<filename>recovery.conf</filename>. If you're planning to use
|
||||
<command>pg_ctl promote</command> or to call
|
||||
<function>pg_promote</function> to fail over,
|
||||
<varname>trigger_file</varname> is not required. If you're
|
||||
setting up the reporting servers that are only used to offload read-only
|
||||
queries from the primary, not for high availability purposes, you don't
|
||||
need to promote it.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
@ -1268,7 +1268,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
|
||||
<entry>Waiting in an extension.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry morerows="33"><literal>IPC</literal></entry>
|
||||
<entry morerows="34"><literal>IPC</literal></entry>
|
||||
<entry><literal>BgWorkerShutdown</literal></entry>
|
||||
<entry>Waiting for background worker to shut down.</entry>
|
||||
</row>
|
||||
@ -1388,6 +1388,10 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
|
||||
<entry><literal>ProcArrayGroupUpdate</literal></entry>
|
||||
<entry>Waiting for group leader to clear transaction id at transaction end.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>Promote</literal></entry>
|
||||
<entry>Waiting for standby promotion.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>ReplicationOriginDrop</literal></entry>
|
||||
<entry>Waiting for a replication origin to become inactive to be dropped.</entry>
|
||||
|
@ -439,7 +439,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
|
||||
<para>
|
||||
Specifies a trigger file whose presence ends recovery in the
|
||||
standby. Even if this value is not set, you can still promote
|
||||
the standby using <command>pg_ctl promote</command>.
|
||||
the standby using <command>pg_ctl promote</command> or calling
|
||||
<function>pg_promote</function>.
|
||||
This setting has no effect if <varname>standby_mode</varname> is <literal>off</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
|
@ -78,12 +78,6 @@
|
||||
|
||||
extern uint32 bootstrap_data_checksum_version;
|
||||
|
||||
/* File path names (all relative to $PGDATA) */
|
||||
#define RECOVERY_COMMAND_FILE "recovery.conf"
|
||||
#define RECOVERY_COMMAND_DONE "recovery.done"
|
||||
#define PROMOTE_SIGNAL_FILE "promote"
|
||||
#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
|
||||
|
||||
|
||||
/* User-settable parameters */
|
||||
int max_wal_size_mb = 1024; /* 1 GB */
|
||||
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "access/htup_details.h"
|
||||
#include "access/xlog.h"
|
||||
#include "access/xlog_internal.h"
|
||||
@ -23,6 +25,7 @@
|
||||
#include "catalog/pg_type.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "replication/walreceiver.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "utils/builtins.h"
|
||||
@ -697,3 +700,77 @@ pg_backup_start_time(PG_FUNCTION_ARGS)
|
||||
|
||||
PG_RETURN_DATUM(xtime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Promotes a standby server.
|
||||
*
|
||||
* A result of "true" means that promotion has been completed if "wait" is
|
||||
* "true", or initiated if "wait" is false.
|
||||
*/
|
||||
Datum
|
||||
pg_promote(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bool wait = PG_GETARG_BOOL(0);
|
||||
int wait_seconds = PG_GETARG_INT32(1);
|
||||
FILE *promote_file;
|
||||
int i;
|
||||
|
||||
if (!RecoveryInProgress())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("recovery is not in progress"),
|
||||
errhint("Recovery control functions can only be executed during recovery.")));
|
||||
|
||||
if (wait_seconds <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("\"wait_seconds\" cannot be negative or equal zero")));
|
||||
|
||||
/* create the promote signal file */
|
||||
promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w");
|
||||
if (!promote_file)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create file \"%s\": %m",
|
||||
PROMOTE_SIGNAL_FILE)));
|
||||
|
||||
if (FreeFile(promote_file))
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not write file \"%s\": %m",
|
||||
PROMOTE_SIGNAL_FILE)));
|
||||
|
||||
/* signal the postmaster */
|
||||
if (kill(PostmasterPid, SIGUSR1) != 0)
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errmsg("failed to send signal to postmaster: %m")));
|
||||
(void) unlink(PROMOTE_SIGNAL_FILE);
|
||||
PG_RETURN_BOOL(false);
|
||||
}
|
||||
|
||||
/* return immediately if waiting was not requested */
|
||||
if (!wait)
|
||||
PG_RETURN_BOOL(true);
|
||||
|
||||
/* wait for the amount of time wanted until promotion */
|
||||
#define WAITS_PER_SECOND 10
|
||||
for (i = 0; i < WAITS_PER_SECOND * wait_seconds; i++)
|
||||
{
|
||||
ResetLatch(MyLatch);
|
||||
|
||||
if (!RecoveryInProgress())
|
||||
PG_RETURN_BOOL(true);
|
||||
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
WaitLatch(MyLatch,
|
||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||
1000L / WAITS_PER_SECOND,
|
||||
WAIT_EVENT_PROMOTE);
|
||||
}
|
||||
|
||||
ereport(WARNING,
|
||||
(errmsg("server did not promote within %d seconds", wait_seconds)));
|
||||
PG_RETURN_BOOL(false);
|
||||
}
|
||||
|
@ -1027,6 +1027,11 @@ CREATE OR REPLACE FUNCTION pg_stop_backup (
|
||||
RETURNS SETOF record STRICT VOLATILE LANGUAGE internal as 'pg_stop_backup_v2'
|
||||
PARALLEL RESTRICTED;
|
||||
|
||||
CREATE OR REPLACE FUNCTION
|
||||
pg_promote(wait boolean DEFAULT true, wait_seconds integer DEFAULT 60)
|
||||
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_promote'
|
||||
PARALLEL RESTRICTED;
|
||||
|
||||
-- legacy definition for compatibility with 9.3
|
||||
CREATE OR REPLACE FUNCTION
|
||||
json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
|
||||
@ -1138,6 +1143,7 @@ REVOKE EXECUTE ON FUNCTION pg_rotate_logfile() FROM public;
|
||||
REVOKE EXECUTE ON FUNCTION pg_reload_conf() FROM public;
|
||||
REVOKE EXECUTE ON FUNCTION pg_current_logfile() FROM public;
|
||||
REVOKE EXECUTE ON FUNCTION pg_current_logfile(text) FROM public;
|
||||
REVOKE EXECUTE ON FUNCTION pg_promote(boolean, integer) FROM public;
|
||||
|
||||
REVOKE EXECUTE ON FUNCTION pg_stat_reset() FROM public;
|
||||
REVOKE EXECUTE ON FUNCTION pg_stat_reset_shared(text) FROM public;
|
||||
|
@ -3663,6 +3663,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
|
||||
case WAIT_EVENT_PROCARRAY_GROUP_UPDATE:
|
||||
event_name = "ProcArrayGroupUpdate";
|
||||
break;
|
||||
case WAIT_EVENT_PROMOTE:
|
||||
event_name = "Promote";
|
||||
break;
|
||||
case WAIT_EVENT_REPLICATION_ORIGIN_DROP:
|
||||
event_name = "ReplicationOriginDrop";
|
||||
break;
|
||||
|
@ -319,10 +319,16 @@ extern void do_pg_abort_backup(void);
|
||||
extern SessionBackupState get_backup_status(void);
|
||||
|
||||
/* File path names (all relative to $PGDATA) */
|
||||
#define RECOVERY_COMMAND_FILE "recovery.conf"
|
||||
#define RECOVERY_COMMAND_DONE "recovery.done"
|
||||
#define BACKUP_LABEL_FILE "backup_label"
|
||||
#define BACKUP_LABEL_OLD "backup_label.old"
|
||||
|
||||
#define TABLESPACE_MAP "tablespace_map"
|
||||
#define TABLESPACE_MAP_OLD "tablespace_map.old"
|
||||
|
||||
/* files to signal promotion to primary */
|
||||
#define PROMOTE_SIGNAL_FILE "promote"
|
||||
#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
|
||||
|
||||
#endif /* XLOG_H */
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201810111
|
||||
#define CATALOG_VERSION_NO 201810251
|
||||
|
||||
#endif
|
||||
|
@ -5824,6 +5824,10 @@
|
||||
proname => 'pg_backup_start_time', provolatile => 's',
|
||||
prorettype => 'timestamptz', proargtypes => '',
|
||||
prosrc => 'pg_backup_start_time' },
|
||||
{ oid => '3436', descr => 'promote standby server',
|
||||
proname => 'pg_promote', provolatile => 'v',
|
||||
prorettype => 'bool', proargtypes => 'bool int4', proargnames => '{wait,wait_seconds}',
|
||||
prosrc => 'pg_promote' },
|
||||
{ oid => '2848', descr => 'switch to new wal file',
|
||||
proname => 'pg_switch_wal', provolatile => 'v', prorettype => 'pg_lsn',
|
||||
proargtypes => '', prosrc => 'pg_switch_wal' },
|
||||
|
@ -829,6 +829,7 @@ typedef enum
|
||||
WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN,
|
||||
WAIT_EVENT_PARALLEL_FINISH,
|
||||
WAIT_EVENT_PROCARRAY_GROUP_UPDATE,
|
||||
WAIT_EVENT_PROMOTE,
|
||||
WAIT_EVENT_REPLICATION_ORIGIN_DROP,
|
||||
WAIT_EVENT_REPLICATION_SLOT_DROP,
|
||||
WAIT_EVENT_SAFE_SNAPSHOT,
|
||||
|
@ -6,7 +6,7 @@ use warnings;
|
||||
use File::Path qw(rmtree);
|
||||
use PostgresNode;
|
||||
use TestLib;
|
||||
use Test::More tests => 1;
|
||||
use Test::More tests => 2;
|
||||
|
||||
$ENV{PGDATABASE} = 'postgres';
|
||||
|
||||
@ -37,9 +37,14 @@ $node_master->safe_psql('postgres',
|
||||
$node_master->wait_for_catchup($node_standby_1, 'replay',
|
||||
$node_master->lsn('write'));
|
||||
|
||||
# Stop and remove master, and promote standby 1, switching it to a new timeline
|
||||
# Stop and remove master
|
||||
$node_master->teardown_node;
|
||||
$node_standby_1->promote;
|
||||
|
||||
# promote standby 1 using "pg_promote", switching it to a new timeline
|
||||
my $psql_out = '';
|
||||
$node_standby_1->psql('postgres', "SELECT pg_promote(wait_seconds => 300)",
|
||||
stdout => \$psql_out);
|
||||
is($psql_out, 't', "promotion of standby with pg_promote");
|
||||
|
||||
# Switch standby 2 to replay from standby 1
|
||||
rmtree($node_standby_2->data_dir . '/recovery.conf');
|
||||
|
Loading…
Reference in New Issue
Block a user