Skip to content

Commit 1007465

Browse files
committed
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
1 parent 0a8590b commit 1007465

File tree

13 files changed

+143
-20
lines changed

13 files changed

+143
-20
lines changed

doc/src/sgml/func.sgml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19202,6 +19202,9 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
1920219202
<indexterm>
1920319203
<primary>pg_is_wal_replay_paused</primary>
1920419204
</indexterm>
19205+
<indexterm>
19206+
<primary>pg_promote</primary>
19207+
</indexterm>
1920519208
<indexterm>
1920619209
<primary>pg_wal_replay_pause</primary>
1920719210
</indexterm>
@@ -19232,6 +19235,22 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
1923219235
<entry>True if recovery is paused.
1923319236
</entry>
1923419237
</row>
19238+
<row>
19239+
<entry>
19240+
<literal><function>pg_promote(<parameter>wait</parameter> <type>boolean</type> DEFAULT true, <parameter>wait_seconds</parameter> <type>integer</type> DEFAULT 60)</function></literal>
19241+
</entry>
19242+
<entry><type>boolean</type></entry>
19243+
<entry>
19244+
Promotes a physical standby server. Returns <literal>true</literal>
19245+
if promotion is successful and <literal>false</literal> otherwise.
19246+
With <parameter>wait</parameter> set to <literal>true</literal>, the
19247+
default, the function waits until promotion is completed or
19248+
<parameter>wait_seconds</parameter> seconds have passed, otherwise the
19249+
function returns immediately after sending the promotion signal to the
19250+
postmaster. This function is restricted to superusers by default, but
19251+
other users can be granted EXECUTE to run the function.
19252+
</entry>
19253+
</row>
1923519254
<row>
1923619255
<entry>
1923719256
<literal><function>pg_wal_replay_pause()</function></literal>

doc/src/sgml/high-availability.sgml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,14 +1471,17 @@ synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
14711471
</para>
14721472

14731473
<para>
1474-
To trigger failover of a log-shipping standby server,
1475-
run <command>pg_ctl promote</command> or create a trigger
1476-
file with the file name and path specified by the <varname>trigger_file</varname>
1477-
setting in <filename>recovery.conf</filename>. If you're planning to use
1478-
<command>pg_ctl promote</command> to fail over, <varname>trigger_file</varname> is
1479-
not required. If you're setting up the reporting servers that are
1480-
only used to offload read-only queries from the primary, not for high
1481-
availability purposes, you don't need to promote it.
1474+
To trigger failover of a log-shipping standby server, run
1475+
<command>pg_ctl promote</command>, call <function>pg_promote</function>,
1476+
or create a trigger file with the file name and path specified by the
1477+
<varname>trigger_file</varname> setting in
1478+
<filename>recovery.conf</filename>. If you're planning to use
1479+
<command>pg_ctl promote</command> or to call
1480+
<function>pg_promote</function> to fail over,
1481+
<varname>trigger_file</varname> is not required. If you're
1482+
setting up the reporting servers that are only used to offload read-only
1483+
queries from the primary, not for high availability purposes, you don't
1484+
need to promote it.
14821485
</para>
14831486
</sect1>
14841487

doc/src/sgml/monitoring.sgml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
12681268
<entry>Waiting in an extension.</entry>
12691269
</row>
12701270
<row>
1271-
<entry morerows="33"><literal>IPC</literal></entry>
1271+
<entry morerows="34"><literal>IPC</literal></entry>
12721272
<entry><literal>BgWorkerShutdown</literal></entry>
12731273
<entry>Waiting for background worker to shut down.</entry>
12741274
</row>
@@ -1388,6 +1388,10 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
13881388
<entry><literal>ProcArrayGroupUpdate</literal></entry>
13891389
<entry>Waiting for group leader to clear transaction id at transaction end.</entry>
13901390
</row>
1391+
<row>
1392+
<entry><literal>Promote</literal></entry>
1393+
<entry>Waiting for standby promotion.</entry>
1394+
</row>
13911395
<row>
13921396
<entry><literal>ReplicationOriginDrop</literal></entry>
13931397
<entry>Waiting for a replication origin to become inactive to be dropped.</entry>

doc/src/sgml/recovery-config.sgml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
439439
<para>
440440
Specifies a trigger file whose presence ends recovery in the
441441
standby. Even if this value is not set, you can still promote
442-
the standby using <command>pg_ctl promote</command>.
442+
the standby using <command>pg_ctl promote</command> or calling
443+
<function>pg_promote</function>.
443444
This setting has no effect if <varname>standby_mode</varname> is <literal>off</literal>.
444445
</para>
445446
</listitem>

src/backend/access/transam/xlog.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,6 @@
7878

7979
extern uint32 bootstrap_data_checksum_version;
8080

81-
/* File path names (all relative to $PGDATA) */
82-
#define RECOVERY_COMMAND_FILE "recovery.conf"
83-
#define RECOVERY_COMMAND_DONE "recovery.done"
84-
#define PROMOTE_SIGNAL_FILE "promote"
85-
#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
86-
8781

8882
/* User-settable parameters */
8983
int max_wal_size_mb = 1024; /* 1 GB */

src/backend/access/transam/xlogfuncs.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
*/
1717
#include "postgres.h"
1818

19+
#include <unistd.h>
20+
1921
#include "access/htup_details.h"
2022
#include "access/xlog.h"
2123
#include "access/xlog_internal.h"
2224
#include "access/xlogutils.h"
2325
#include "catalog/pg_type.h"
2426
#include "funcapi.h"
2527
#include "miscadmin.h"
28+
#include "pgstat.h"
2629
#include "replication/walreceiver.h"
2730
#include "storage/smgr.h"
2831
#include "utils/builtins.h"
@@ -697,3 +700,77 @@ pg_backup_start_time(PG_FUNCTION_ARGS)
697700

698701
PG_RETURN_DATUM(xtime);
699702
}
703+
704+
/*
705+
* Promotes a standby server.
706+
*
707+
* A result of "true" means that promotion has been completed if "wait" is
708+
* "true", or initiated if "wait" is false.
709+
*/
710+
Datum
711+
pg_promote(PG_FUNCTION_ARGS)
712+
{
713+
bool wait = PG_GETARG_BOOL(0);
714+
int wait_seconds = PG_GETARG_INT32(1);
715+
FILE *promote_file;
716+
int i;
717+
718+
if (!RecoveryInProgress())
719+
ereport(ERROR,
720+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
721+
errmsg("recovery is not in progress"),
722+
errhint("Recovery control functions can only be executed during recovery.")));
723+
724+
if (wait_seconds <= 0)
725+
ereport(ERROR,
726+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
727+
errmsg("\"wait_seconds\" cannot be negative or equal zero")));
728+
729+
/* create the promote signal file */
730+
promote_file = AllocateFile(PROMOTE_SIGNAL_FILE, "w");
731+
if (!promote_file)
732+
ereport(ERROR,
733+
(errcode_for_file_access(),
734+
errmsg("could not create file \"%s\": %m",
735+
PROMOTE_SIGNAL_FILE)));
736+
737+
if (FreeFile(promote_file))
738+
ereport(ERROR,
739+
(errcode_for_file_access(),
740+
errmsg("could not write file \"%s\": %m",
741+
PROMOTE_SIGNAL_FILE)));
742+
743+
/* signal the postmaster */
744+
if (kill(PostmasterPid, SIGUSR1) != 0)
745+
{
746+
ereport(WARNING,
747+
(errmsg("failed to send signal to postmaster: %m")));
748+
(void) unlink(PROMOTE_SIGNAL_FILE);
749+
PG_RETURN_BOOL(false);
750+
}
751+
752+
/* return immediately if waiting was not requested */
753+
if (!wait)
754+
PG_RETURN_BOOL(true);
755+
756+
/* wait for the amount of time wanted until promotion */
757+
#define WAITS_PER_SECOND 10
758+
for (i = 0; i < WAITS_PER_SECOND * wait_seconds; i++)
759+
{
760+
ResetLatch(MyLatch);
761+
762+
if (!RecoveryInProgress())
763+
PG_RETURN_BOOL(true);
764+
765+
CHECK_FOR_INTERRUPTS();
766+
767+
WaitLatch(MyLatch,
768+
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
769+
1000L / WAITS_PER_SECOND,
770+
WAIT_EVENT_PROMOTE);
771+
}
772+
773+
ereport(WARNING,
774+
(errmsg("server did not promote within %d seconds", wait_seconds)));
775+
PG_RETURN_BOOL(false);
776+
}

src/backend/catalog/system_views.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,11 @@ CREATE OR REPLACE FUNCTION pg_stop_backup (
10271027
RETURNS SETOF record STRICT VOLATILE LANGUAGE internal as 'pg_stop_backup_v2'
10281028
PARALLEL RESTRICTED;
10291029

1030+
CREATE OR REPLACE FUNCTION
1031+
pg_promote(wait boolean DEFAULT true, wait_seconds integer DEFAULT 60)
1032+
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_promote'
1033+
PARALLEL RESTRICTED;
1034+
10301035
-- legacy definition for compatibility with 9.3
10311036
CREATE OR REPLACE FUNCTION
10321037
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;
11381143
REVOKE EXECUTE ON FUNCTION pg_reload_conf() FROM public;
11391144
REVOKE EXECUTE ON FUNCTION pg_current_logfile() FROM public;
11401145
REVOKE EXECUTE ON FUNCTION pg_current_logfile(text) FROM public;
1146+
REVOKE EXECUTE ON FUNCTION pg_promote(boolean, integer) FROM public;
11411147

11421148
REVOKE EXECUTE ON FUNCTION pg_stat_reset() FROM public;
11431149
REVOKE EXECUTE ON FUNCTION pg_stat_reset_shared(text) FROM public;

src/backend/postmaster/pgstat.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,6 +3663,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
36633663
case WAIT_EVENT_PROCARRAY_GROUP_UPDATE:
36643664
event_name = "ProcArrayGroupUpdate";
36653665
break;
3666+
case WAIT_EVENT_PROMOTE:
3667+
event_name = "Promote";
3668+
break;
36663669
case WAIT_EVENT_REPLICATION_ORIGIN_DROP:
36673670
event_name = "ReplicationOriginDrop";
36683671
break;

src/include/access/xlog.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,16 @@ extern void do_pg_abort_backup(void);
319319
extern SessionBackupState get_backup_status(void);
320320

321321
/* File path names (all relative to $PGDATA) */
322+
#define RECOVERY_COMMAND_FILE "recovery.conf"
323+
#define RECOVERY_COMMAND_DONE "recovery.done"
322324
#define BACKUP_LABEL_FILE "backup_label"
323325
#define BACKUP_LABEL_OLD "backup_label.old"
324326

325327
#define TABLESPACE_MAP "tablespace_map"
326328
#define TABLESPACE_MAP_OLD "tablespace_map.old"
327329

330+
/* files to signal promotion to primary */
331+
#define PROMOTE_SIGNAL_FILE "promote"
332+
#define FALLBACK_PROMOTE_SIGNAL_FILE "fallback_promote"
333+
328334
#endif /* XLOG_H */

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201810111
56+
#define CATALOG_VERSION_NO 201810251
5757

5858
#endif

src/include/catalog/pg_proc.dat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5824,6 +5824,10 @@
58245824
proname => 'pg_backup_start_time', provolatile => 's',
58255825
prorettype => 'timestamptz', proargtypes => '',
58265826
prosrc => 'pg_backup_start_time' },
5827+
{ oid => '3436', descr => 'promote standby server',
5828+
proname => 'pg_promote', provolatile => 'v',
5829+
prorettype => 'bool', proargtypes => 'bool int4', proargnames => '{wait,wait_seconds}',
5830+
prosrc => 'pg_promote' },
58275831
{ oid => '2848', descr => 'switch to new wal file',
58285832
proname => 'pg_switch_wal', provolatile => 'v', prorettype => 'pg_lsn',
58295833
proargtypes => '', prosrc => 'pg_switch_wal' },

src/include/pgstat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,7 @@ typedef enum
829829
WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN,
830830
WAIT_EVENT_PARALLEL_FINISH,
831831
WAIT_EVENT_PROCARRAY_GROUP_UPDATE,
832+
WAIT_EVENT_PROMOTE,
832833
WAIT_EVENT_REPLICATION_ORIGIN_DROP,
833834
WAIT_EVENT_REPLICATION_SLOT_DROP,
834835
WAIT_EVENT_SAFE_SNAPSHOT,

src/test/recovery/t/004_timeline_switch.pl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use File::Path qw(rmtree);
77
use PostgresNode;
88
use TestLib;
9-
use Test::More tests => 1;
9+
use Test::More tests => 2;
1010

1111
$ENV{PGDATABASE} = 'postgres';
1212

@@ -37,9 +37,14 @@
3737
$node_master->wait_for_catchup($node_standby_1, 'replay',
3838
$node_master->lsn('write'));
3939

40-
# Stop and remove master, and promote standby 1, switching it to a new timeline
40+
# Stop and remove master
4141
$node_master->teardown_node;
42-
$node_standby_1->promote;
42+
43+
# promote standby 1 using "pg_promote", switching it to a new timeline
44+
my $psql_out = '';
45+
$node_standby_1->psql('postgres', "SELECT pg_promote(wait_seconds => 300)",
46+
stdout => \$psql_out);
47+
is($psql_out, 't', "promotion of standby with pg_promote");
4348

4449
# Switch standby 2 to replay from standby 1
4550
rmtree($node_standby_2->data_dir . '/recovery.conf');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy