Masahiko Sawada wrote: > Maybe the patch needs regression tests for the new function. And I'd > suggest to make the function name more clear by changing to > pg_promote_server(), pg_promote_standby() and so on.
Thanks for the review. The attached patch has regression tests - I though it would be good to change some of the existing tests that run standby promotion to use the SQL function instead of pg_ctl. I have left the name though -- as far as I can tell, "promote" has no other meaning in PostgreSQL than standby promotion, and I believe it is only good to be more verbose if that avoids confusion. Yours, Laurenz Albe
From a5de6f9893e049bf97810e41530907e237f909d7 Mon Sep 17 00:00:00 2001 From: Laurenz Albe <laurenz.a...@cybertec.at> 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); <indexterm> <primary>pg_terminate_backend</primary> </indexterm> + <indexterm> + <primary>pg_promote</primary> + </indexterm> <indexterm> <primary>signal</primary> @@ -18790,6 +18793,16 @@ SELECT set_config('log_statement_stats', 'off', false); however only superusers can terminate superuser backends. </entry> </row> + <row> + <entry> + <literal><function>pg_promote()</function></literal> + </entry> + <entry><type>boolean</type></entry> + <entry>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. + </entry> + </row> </tbody> </tgroup> </table> @@ -18827,6 +18840,13 @@ SELECT set_config('log_statement_stats', 'off', false); subprocess. </para> + <para> + <function>pg_promote()</function> 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. + </para> + </sect2> <sect2 id="functions-admin-backup"> 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)' <para> To trigger failover of a log-shipping standby server, - run <command>pg_ctl promote</command> or create a trigger + 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> to fail over, <varname>trigger_file</varname> 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 <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> 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 <unistd.h> /* * 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