Updated patch set for pg_ctl promote -w, incorporating AFAICT all of the previous reviews, in particular Michael Paquier's changes to the StartupXLOG() ordering, and rebasing on top of src/common/controldata_utils.c.
-- Peter Eisentraut http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From 371ca61ab87e792864ac2f76f3e0dad7912c247e Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Tue, 26 Jul 2016 11:39:43 -0400 Subject: [PATCH 1/5] Make command_like output more compact Consistently print the test name, not the full command, which can be quite lenghty and include temporary directory names and other distracting details. --- src/test/perl/TestLib.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm index 649fd82..c7b3262 100644 --- a/src/test/perl/TestLib.pm +++ b/src/test/perl/TestLib.pm @@ -276,8 +276,8 @@ sub command_like my ($stdout, $stderr); print("# Running: " . join(" ", @{$cmd}) . "\n"); my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr; - ok($result, "@$cmd exit code 0"); - is($stderr, '', "@$cmd no stderr"); + ok($result, "$test_name: exit code 0"); + is($stderr, '', "$test_name: no stderr"); like($stdout, $expected_stdout, "$test_name: matches"); } -- 2.9.2
>From eabd099bf09d9a4815c29c730d568161c87a67ba Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Tue, 26 Jul 2016 10:48:43 -0400 Subject: [PATCH 2/5] pg_ctl: Add tests for promote action --- src/bin/pg_ctl/t/003_promote.pl | 41 +++++++++++++++++++++++++++++++++++++++++ src/test/perl/TestLib.pm | 11 +++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/bin/pg_ctl/t/003_promote.pl diff --git a/src/bin/pg_ctl/t/003_promote.pl b/src/bin/pg_ctl/t/003_promote.pl new file mode 100644 index 0000000..d7e7b99 --- /dev/null +++ b/src/bin/pg_ctl/t/003_promote.pl @@ -0,0 +1,41 @@ +use strict; +use warnings; + +use PostgresNode; +use TestLib; +use Test::More tests => 9; + +my $tempdir = TestLib::tempdir; + +command_fails_like([ 'pg_ctl', 'promote', '-D', "$tempdir/nonexistent" ], + qr/directory .* does not exist/, + 'pg_ctl promote with nonexistent directory'); + +my $node_primary = get_new_node('primary'); +$node_primary->init(allows_streaming => 1); + +command_fails_like([ 'pg_ctl', 'promote', '-D', $node_primary->data_dir ], + qr/PID file .* does not exist/, + 'pg_ctl promote of not running instance fails'); + +$node_primary->start; + +command_fails_like([ 'pg_ctl', 'promote', '-D', $node_primary->data_dir ], + qr/not in standby mode/, + 'pg_ctl promote of primary instance fails'); + +my $node_standby = get_new_node('standby'); +$node_primary->backup('my_backup'); +$node_standby->init_from_backup($node_primary, 'my_backup', has_streaming => 1); +$node_standby->start; + +is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), + 't', 'standby is in recovery'); + +command_ok([ 'pg_ctl', 'promote', '-D', $node_standby->data_dir ], + 'pg_ctl promote of standby runs'); + +$node_standby->poll_query_until('postgres', 'SELECT NOT pg_is_in_recovery()'); + +is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), + 'f', 'promoted standby is not in recovery'); diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm index c7b3262..51b533e 100644 --- a/src/test/perl/TestLib.pm +++ b/src/test/perl/TestLib.pm @@ -34,6 +34,7 @@ our @EXPORT = qw( program_version_ok program_options_handling_ok command_like + command_fails_like $windows_os ); @@ -281,4 +282,14 @@ sub command_like like($stdout, $expected_stdout, "$test_name: matches"); } +sub command_fails_like +{ + my ($cmd, $expected_stderr, $test_name) = @_; + my ($stdout, $stderr); + print("# Running: " . join(" ", @{$cmd}) . "\n"); + my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr; + ok(!$result, "$test_name: exit code not 0"); + like($stderr, $expected_stderr, "$test_name: matches"); +} + 1; -- 2.9.2
>From b5bc405a85c6a84c7916f5cc2c3225438df70315 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Tue, 26 Jul 2016 11:23:43 -0400 Subject: [PATCH 3/5] pg_ctl: Detect current standby state from pg_control pg_ctl used to determine whether a server was in standby mode by looking for a recovery.conf file. With this change, it instead looks into pg_control, which is potentially more accurate. There are also occasional discussions about removing recovery.conf, so this removes one dependency. Add a wait_seconds argument to get_controlfile() so that pg_ctl can wait a bit for a consistent pg_control file. Otherwise, pg_ctl operations might fail on rare occasions when the server is updating the file at the same time. --- src/backend/utils/misc/pg_controldata.c | 8 ++-- src/bin/pg_controldata/pg_controldata.c | 2 +- src/bin/pg_ctl/pg_ctl.c | 35 ++++++++------ src/common/controldata_utils.c | 83 ++++++++++++++++++++------------- src/include/common/controldata_utils.h | 2 +- 5 files changed, 76 insertions(+), 54 deletions(-) diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c index 34ee76a..13f0523 100644 --- a/src/backend/utils/misc/pg_controldata.c +++ b/src/backend/utils/misc/pg_controldata.c @@ -51,7 +51,7 @@ pg_control_system(PG_FUNCTION_ARGS) tupdesc = BlessTupleDesc(tupdesc); /* read the control file */ - ControlFile = get_controlfile(DataDir, NULL); + ControlFile = get_controlfile(DataDir, NULL, NULL); values[0] = Int32GetDatum(ControlFile->pg_control_version); nulls[0] = false; @@ -127,7 +127,7 @@ pg_control_checkpoint(PG_FUNCTION_ARGS) tupdesc = BlessTupleDesc(tupdesc); /* Read the control file. */ - ControlFile = get_controlfile(DataDir, NULL); + ControlFile = get_controlfile(DataDir, NULL, NULL); /* * Calculate name of the WAL file containing the latest checkpoint's REDO @@ -229,7 +229,7 @@ pg_control_recovery(PG_FUNCTION_ARGS) tupdesc = BlessTupleDesc(tupdesc); /* read the control file */ - ControlFile = get_controlfile(DataDir, NULL); + ControlFile = get_controlfile(DataDir, NULL, NULL); values[0] = LSNGetDatum(ControlFile->minRecoveryPoint); nulls[0] = false; @@ -294,7 +294,7 @@ pg_control_init(PG_FUNCTION_ARGS) tupdesc = BlessTupleDesc(tupdesc); /* read the control file */ - ControlFile = get_controlfile(DataDir, NULL); + ControlFile = get_controlfile(DataDir, NULL, NULL); values[0] = Int32GetDatum(ControlFile->maxAlign); nulls[0] = false; diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 96619a2..c107601 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -155,7 +155,7 @@ main(int argc, char *argv[]) } /* get a copy of the control file */ - ControlFile = get_controlfile(DataDir, progname); + ControlFile = get_controlfile(DataDir, progname, NULL); /* * This slightly-chintzy coding will work as long as the control file diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index efc0729..fac3986 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -19,6 +19,8 @@ #include "postgres_fe.h" +#include "catalog/pg_control.h" +#include "common/controldata_utils.h" #include "libpq-fe.h" #include "pqexpbuffer.h" @@ -96,7 +98,6 @@ static char postopts_file[MAXPGPATH]; static char version_file[MAXPGPATH]; static char pid_file[MAXPGPATH]; static char backup_file[MAXPGPATH]; -static char recovery_file[MAXPGPATH]; static char promote_file[MAXPGPATH]; #ifdef WIN32 @@ -988,15 +989,17 @@ do_stop(void) /* * If backup_label exists, an online backup is running. Warn the user * that smart shutdown will wait for it to finish. However, if - * recovery.conf is also present, we're recovering from an online + * the server is in archive recovery, we're recovering from an online * backup instead of performing one. */ if (shutdown_mode == SMART_MODE && - stat(backup_file, &statbuf) == 0 && - stat(recovery_file, &statbuf) != 0) + stat(backup_file, &statbuf) == 0) { - print_msg(_("WARNING: online backup mode is active\n" - "Shutdown will not complete until pg_stop_backup() is called.\n\n")); + ControlFileData *control_file_data = get_controlfile(pg_data, progname, &wait_seconds); + if (control_file_data->state != DB_IN_ARCHIVE_RECOVERY) + print_msg(_("WARNING: online backup mode is active\n" + "Shutdown will not complete until pg_stop_backup() is called.\n\n")); + pfree(control_file_data); } print_msg(_("waiting for server to shut down...")); @@ -1076,15 +1079,17 @@ do_restart(void) /* * If backup_label exists, an online backup is running. Warn the user * that smart shutdown will wait for it to finish. However, if - * recovery.conf is also present, we're recovering from an online + * the server is in archive recovery, we're recovering from an online * backup instead of performing one. */ if (shutdown_mode == SMART_MODE && - stat(backup_file, &statbuf) == 0 && - stat(recovery_file, &statbuf) != 0) + stat(backup_file, &statbuf) == 0) { - print_msg(_("WARNING: online backup mode is active\n" - "Shutdown will not complete until pg_stop_backup() is called.\n\n")); + ControlFileData *control_file_data = get_controlfile(pg_data, progname, &wait_seconds); + if (control_file_data->state != DB_IN_ARCHIVE_RECOVERY) + print_msg(_("WARNING: online backup mode is active\n" + "Shutdown will not complete until pg_stop_backup() is called.\n\n")); + pfree(control_file_data); } print_msg(_("waiting for server to shut down...")); @@ -1168,7 +1173,7 @@ do_promote(void) { FILE *prmfile; pgpid_t pid; - struct stat statbuf; + ControlFileData *control_file_data; pid = get_pgpid(false); @@ -1187,14 +1192,15 @@ do_promote(void) exit(1); } - /* If recovery.conf doesn't exist, the server is not in standby mode */ - if (stat(recovery_file, &statbuf) != 0) + control_file_data = get_controlfile(pg_data, progname, &wait_seconds); + if (control_file_data->state != DB_IN_ARCHIVE_RECOVERY) { write_stderr(_("%s: cannot promote server; " "server is not in standby mode\n"), progname); exit(1); } + pfree(control_file_data); /* * For 9.3 onwards, "fast" promotion is performed. Promotion with a full @@ -2401,7 +2407,6 @@ main(int argc, char **argv) snprintf(version_file, MAXPGPATH, "%s/PG_VERSION", pg_data); snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data); snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data); - snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data); } switch (ctl_command) diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c index 5592fe7..01002b0 100644 --- a/src/common/controldata_utils.c +++ b/src/common/controldata_utils.c @@ -35,60 +35,77 @@ * for pfreeing the result. */ ControlFileData * -get_controlfile(char *DataDir, const char *progname) +get_controlfile(const char *DataDir, const char *progname, int *wait_seconds) { ControlFileData *ControlFile; - int fd; char ControlFilePath[MAXPGPATH]; - pg_crc32c crc; ControlFile = palloc(sizeof(ControlFileData)); snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir); - if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1) + for (;;) + { + int fd; + pg_crc32c crc; + + if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1) #ifndef FRONTEND - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not open file \"%s\" for reading: %m", - ControlFilePath))); + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open file \"%s\" for reading: %m", + ControlFilePath))); #else - { - fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), - progname, ControlFilePath, strerror(errno)); - exit(EXIT_FAILURE); - } + { + fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), + progname, ControlFilePath, strerror(errno)); + exit(EXIT_FAILURE); + } #endif - if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData)) + if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData)) #ifndef FRONTEND - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", ControlFilePath))); + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file \"%s\": %m", ControlFilePath))); #else - { - fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), - progname, ControlFilePath, strerror(errno)); - exit(EXIT_FAILURE); - } + { + fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), + progname, ControlFilePath, strerror(errno)); + exit(EXIT_FAILURE); + } #endif - close(fd); + close(fd); + + /* Check the CRC. */ + INIT_CRC32C(crc); + COMP_CRC32C(crc, + (char *) ControlFile, + offsetof(ControlFileData, crc)); + FIN_CRC32C(crc); - /* Check the CRC. */ - INIT_CRC32C(crc); - COMP_CRC32C(crc, - (char *) ControlFile, - offsetof(ControlFileData, crc)); - FIN_CRC32C(crc); + if (EQ_CRC32C(crc, ControlFile->crc)) + break; + + if (wait_seconds && *wait_seconds > 0) + { + sleep(1); + (*wait_seconds)--; + continue; + } - if (!EQ_CRC32C(crc, ControlFile->crc)) #ifndef FRONTEND elog(ERROR, _("calculated CRC checksum does not match value stored in file")); #else - printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n" - "Either the file is corrupt, or it has a different layout than this program\n" - "is expecting. The results below are untrustworthy.\n\n")); + if (wait_seconds) + fprintf(stderr, _("%s: control file \"%s\" appears to be corrupt\n"), + progname, ControlFilePath); + else + printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n" + "Either the file is corrupt, or it has a different layout than this program\n" + "is expecting. The results below are untrustworthy.\n\n")); #endif + } /* Make sure the control file is valid byte order. */ if (ControlFile->pg_control_version % 65536 == 0 && diff --git a/src/include/common/controldata_utils.h b/src/include/common/controldata_utils.h index a355d22..283ec55 100644 --- a/src/include/common/controldata_utils.h +++ b/src/include/common/controldata_utils.h @@ -12,6 +12,6 @@ #include "catalog/pg_control.h" -extern ControlFileData *get_controlfile(char *DataDir, const char *progname); +extern ControlFileData *get_controlfile(const char *DataDir, const char *progname, int *wait_seconds); #endif /* COMMON_CONTROLDATA_UTILS_H */ -- 2.9.2
>From af156fbcb2e0e59045f814f4dd3d3494d0b2078d Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Tue, 26 Jul 2016 12:08:49 -0400 Subject: [PATCH 4/5] Delay updating control file to "in production" Move the updating of the control file to "in production" status until the point where WAL writes are allowed. Before, there could be a significant gap between the control file update and write transactions actually being allowed. This makes it more reliable to use the control status to verify the end of a promotion. Michael Paquier --- src/backend/access/transam/xlog.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f13f9c1..1f8d054 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7349,12 +7349,6 @@ StartupXLOG(void) */ InRecovery = false; - LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); - ControlFile->state = DB_IN_PRODUCTION; - ControlFile->time = (pg_time_t) time(NULL); - UpdateControlFile(); - LWLockRelease(ControlFileLock); - /* start the archive_timeout timer running */ XLogCtl->lastSegSwitchTime = (pg_time_t) time(NULL); @@ -7412,15 +7406,32 @@ StartupXLOG(void) CompleteCommitTsInitialization(); /* - * All done. Allow backends to write WAL. (Although the bool flag is - * probably atomic in itself, we use the info_lck here to ensure that - * there are no race conditions concerning visibility of other recent - * updates to shared memory.) + * All done with end-of-recovery actions. + * + * Now allow backends to write WAL and update the control file status in + * consequence. The boolean flag allowing backends to write WAL is + * updated while holding ControlFileLock to prevent other backends to look + * at an inconsistent state of the control file in shared memory. There + * is still a small window during which backends can write WAL and the + * control file is still referring to a system not in DB_IN_PRODUCTION + * state while looking at the on-disk control file. + * + * Also, although the boolean flag to allow WAL is probably atomic in + * itself, we use the info_lck here to ensure that there are no race + * conditions concerning visibility of other recent updates to shared + * memory. */ + LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); + ControlFile->state = DB_IN_PRODUCTION; + ControlFile->time = (pg_time_t) time(NULL); + SpinLockAcquire(&XLogCtl->info_lck); XLogCtl->SharedRecoveryInProgress = false; SpinLockRelease(&XLogCtl->info_lck); + UpdateControlFile(); + LWLockRelease(ControlFileLock); + /* * If there were cascading standby servers connected to us, nudge any wal * sender processes to notice that we've been promoted. -- 2.9.2
>From 384083f9189dbd691a57f98dfefbf4c836b2d0b6 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Tue, 26 Jul 2016 12:44:08 -0400 Subject: [PATCH 5/5] pg_ctl: Add wait option to promote action When waiting is selected for the promote action, look into pg_control until the state changes, then use the PQping-based waiting until the server is reachable. --- doc/src/sgml/ref/pg_ctl-ref.sgml | 29 ++++++++++++++++++----- src/bin/pg_ctl/pg_ctl.c | 50 +++++++++++++++++++++++++++++----------- src/bin/pg_ctl/t/003_promote.pl | 18 ++++++++++++++- 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml index 6ceb781..a00c355 100644 --- a/doc/src/sgml/ref/pg_ctl-ref.sgml +++ b/doc/src/sgml/ref/pg_ctl-ref.sgml @@ -91,6 +91,8 @@ <cmdsynopsis> <command>pg_ctl</command> <arg choice="plain"><option>promote</option></arg> + <arg choice="opt"><option>-w</option></arg> + <arg choice="opt"><option>-t</option> <replaceable>seconds</replaceable></arg> <arg choice="opt"><option>-s</option></arg> <arg choice="opt"><option>-D</option> <replaceable>datadir</replaceable></arg> </cmdsynopsis> @@ -361,8 +363,8 @@ <title>Options</title> <term><option>--timeout</option></term> <listitem> <para> - The maximum number of seconds to wait when waiting for startup or - shutdown to complete. Defaults to the value of the + The maximum number of seconds to wait when waiting for an operation + to complete (see option <option>-w</option>). Defaults to the value of the <envar>PGCTLTIMEOUT</> environment variable or, if not set, to 60 seconds. </para> @@ -383,8 +385,23 @@ <title>Options</title> <term><option>-w</option></term> <listitem> <para> - Wait for the startup or shutdown to complete. - Waiting is the default option for shutdowns, but not startups. + Wait for an operation to complete. This is supported for the + modes <literal>start</literal>, <literal>stop</literal>, + <literal>restart</literal>, <literal>promote</literal>, + and <literal>register</literal>. + </para> + + <para> + Waiting is the default option for shutdowns, but not startups, + restarts, or promotions. This is mainly for historical reasons; the + waiting option is almost always preferable. If waiting is not + selected, the requested action is triggered, but there is no feedback + about its success. In that case, the server log file or an external + monitoring system would have to be used to check the progress and + success of the operation. + </para> + + <para> When waiting for startup, <command>pg_ctl</command> repeatedly attempts to connect to the server. When waiting for shutdown, <command>pg_ctl</command> waits for @@ -400,8 +417,8 @@ <title>Options</title> <term><option>-W</option></term> <listitem> <para> - Do not wait for startup or shutdown to complete. This is the - default for start and restart modes. + Do not wait for an operation to complete. This is the opposite of the + option <option>-w</option>. </para> </listitem> </varlistentry> diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index fac3986..87cdcf3 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -1233,7 +1233,39 @@ do_promote(void) exit(1); } - print_msg(_("server promoting\n")); + if (do_wait) + { + DBState state = DB_STARTUP; + + print_msg(_("waiting for server to promote...")); + while (wait_seconds > 0) + { + ControlFileData *control_file_data; + + control_file_data = get_controlfile(pg_data, progname, &wait_seconds); + state = control_file_data->state; + pfree(control_file_data); + + if (state == DB_IN_PRODUCTION) + break; + + print_msg("."); + pg_usleep(1000000); /* 1 sec */ + wait_seconds--; + } + if (state == DB_IN_PRODUCTION) + { + print_msg(_(" done\n")); + print_msg(_("server promoted\n")); + } + else + { + print_msg(_(" stopped waiting\n")); + print_msg(_("server is still promoting\n")); + } + } + else + print_msg(_("server promoting\n")); } @@ -2381,18 +2413,10 @@ main(int argc, char **argv) if (!wait_set) { - switch (ctl_command) - { - case RESTART_COMMAND: - case START_COMMAND: - do_wait = false; - break; - case STOP_COMMAND: - do_wait = true; - break; - default: - break; - } + if (ctl_command == STOP_COMMAND) + do_wait = true; + else + do_wait = false; } if (ctl_command == RELOAD_COMMAND) diff --git a/src/bin/pg_ctl/t/003_promote.pl b/src/bin/pg_ctl/t/003_promote.pl index d7e7b99..6443926 100644 --- a/src/bin/pg_ctl/t/003_promote.pl +++ b/src/bin/pg_ctl/t/003_promote.pl @@ -3,7 +3,7 @@ use PostgresNode; use TestLib; -use Test::More tests => 9; +use Test::More tests => 12; my $tempdir = TestLib::tempdir; @@ -39,3 +39,19 @@ is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), 'f', 'promoted standby is not in recovery'); + +# same again with wait option +$node_standby = get_new_node('standby2'); +$node_standby->init_from_backup($node_primary, 'my_backup', has_streaming => 1); +$node_standby->start; + +is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), + 't', 'standby is in recovery'); + +command_ok([ 'pg_ctl', 'promote', '-w', '-D', $node_standby->data_dir ], + 'pg_ctl promote -w of standby runs'); + +# no wait here + +is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), + 'f', 'promoted standby is not in recovery'); -- 2.9.2
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers