From a5de6f9893e049bf97810e41530907e237f909d7 Mon Sep 17 00:00:00 2001 From: Laurenz Albe Date: Mon, 8 Oct 2018 17:59:37 +0200 Subject: [PATCH] Add pg_promote() to promote standby servers --- doc/src/sgml/func.sgml | 20 +++++++++ doc/src/sgml/high-availability.sgml | 2 +- doc/src/sgml/recovery-config.sgml | 3 +- src/backend/access/transam/xlog.c | 2 - src/backend/access/transam/xlogfuncs.c | 48 ++++++++++++++++++++++ src/include/access/xlog.h | 4 ++ src/include/catalog/pg_proc.dat | 4 ++ src/test/perl/PostgresNode.pm | 22 ++++++++++ src/test/recovery/t/004_timeline_switch.pl | 2 +- src/test/recovery/t/009_twophase.pl | 2 +- 10 files changed, 103 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 9a7f683658..ae8a9b4ccb 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18731,6 +18731,9 @@ SELECT set_config('log_statement_stats', 'off', false); pg_terminate_backend + + pg_promote + signal @@ -18790,6 +18793,16 @@ SELECT set_config('log_statement_stats', 'off', false); however only superusers can terminate superuser backends. + + + pg_promote() + + boolean + Promote a physical standby server. This function can only be + called by superusers and will only have an effect when run on + a standby server. + + @@ -18827,6 +18840,13 @@ SELECT set_config('log_statement_stats', 'off', false); subprocess. + + pg_promote() sends a signal to the server that causes it + to leave standby mode. Since sending signals is by nature asynchronous, + successful execution of the function does not guarantee that the server has + already been promoted. + + diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml index 6f57362df7..8ccd1ffcd1 100644 --- a/doc/src/sgml/high-availability.sgml +++ b/doc/src/sgml/high-availability.sgml @@ -1472,7 +1472,7 @@ synchronous_standby_names = 'ANY 2 (s1, s2, s3)' To trigger failover of a log-shipping standby server, - run pg_ctl promote or create a trigger + run pg_ctl promote, call pg_promote(), or create a trigger file with the file name and path specified by the trigger_file setting in recovery.conf. If you're planning to use pg_ctl promote to fail over, trigger_file is diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml index 92825fdf19..d06cd0b08e 100644 --- a/doc/src/sgml/recovery-config.sgml +++ b/doc/src/sgml/recovery-config.sgml @@ -439,7 +439,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows 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 pg_ctl promote. + the standby using pg_ctl promote or calling + pg_promote(). This setting has no effect if standby_mode is off. diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 7375a78ffc..3a1f49e83a 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -81,8 +81,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 */ diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 9731742978..b448d0b515 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -35,6 +35,7 @@ #include "storage/fd.h" #include "storage/ipc.h" +#include /* * Store label file and tablespace map during non-exclusive backups. @@ -697,3 +698,50 @@ pg_backup_start_time(PG_FUNCTION_ARGS) PG_RETURN_DATUM(xtime); } + +/* + * Promote a standby server. + * + * A result of "true" means that promotion has been initiated. + */ +Datum +pg_promote(PG_FUNCTION_ARGS) +{ + FILE *promote_file; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to promote standby servers"))); + + if (!RecoveryInProgress()) + PG_RETURN_BOOL(false); + + /* create the promote signal file */ + promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w"); + if (!promote_file) + { + ereport(WARNING, + (errmsg("could not create file \"%s\": %m", PROMOTE_SIGNAL_FILE))); + PG_RETURN_BOOL(false); + } + + if (FreeFile(promote_file)) + { + /* probably unreachable, but it is better to be safe */ + ereport(WARNING, + (errmsg("could not write to file \"%s\": %m", PROMOTE_SIGNAL_FILE))); + PG_RETURN_BOOL(false); + } + + /* 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); + } + + PG_RETURN_BOOL(true); +} diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 421ba6d775..a5a3c59007 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -192,6 +192,10 @@ extern bool XLOG_DEBUG; #define XLOG_INCLUDE_ORIGIN 0x01 /* include the replication origin */ #define XLOG_MARK_UNIMPORTANT 0x02 /* record not important for durability */ +/* files to signal promotion to primary */ +#define PROMOTE_SIGNAL_FILE "promote" +#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote" + /* Checkpoint statistics */ typedef struct CheckpointStatsData diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 8e4145f42b..99d494806a 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5997,6 +5997,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 => '', + 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' }, diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm index ae3d8ee10c..4eeff00c92 100644 --- a/src/test/perl/PostgresNode.pm +++ b/src/test/perl/PostgresNode.pm @@ -806,6 +806,28 @@ sub promote =pod +=item $node->promote_sql() + +Wrapper for "SELECT pg_promote()" + +=cut + +sub promote_sql +{ + my ($self) = @_; + my $name = $self->name; + print "### Promoting node \"$name\"\n"; + $self->safe_psql('postgres', "SELECT pg_promote()"); + # pg_promote() is asynchronous, hence we must wait until promotion is complete + while ($self->safe_psql('postgres', "SELECT pg_is_in_recovery()") != 'f') + { + sleep 1; + } + return; +} + +=pod + =item $node->logrotate() Wrapper for pg_ctl logrotate diff --git a/src/test/recovery/t/004_timeline_switch.pl b/src/test/recovery/t/004_timeline_switch.pl index 34ee335129..107ba2b316 100644 --- a/src/test/recovery/t/004_timeline_switch.pl +++ b/src/test/recovery/t/004_timeline_switch.pl @@ -39,7 +39,7 @@ $node_master->wait_for_catchup($node_standby_1, 'replay', # Stop and remove master, and promote standby 1, switching it to a new timeline $node_master->teardown_node; -$node_standby_1->promote; +$node_standby_1->promote_sql; # Switch standby 2 to replay from standby 1 rmtree($node_standby_2->data_dir . '/recovery.conf'); diff --git a/src/test/recovery/t/009_twophase.pl b/src/test/recovery/t/009_twophase.pl index 9ea3bd65fc..197d18dd18 100644 --- a/src/test/recovery/t/009_twophase.pl +++ b/src/test/recovery/t/009_twophase.pl @@ -214,7 +214,7 @@ $cur_master->psql( INSERT INTO t_009_tbl VALUES (22, 'issued to ${cur_master_name}'); PREPARE TRANSACTION 'xact_009_10';"); $cur_master->teardown_node; -$cur_standby->promote; +$cur_standby->promote_sql; # change roles note "Now paris is master and london is standby"; -- 2.17.1