mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03: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:
@ -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');
|
||||
|
Reference in New Issue
Block a user