New trigger option of pg_standby
Hi,
Current pg_standby is dangerous because the presence of the trigger
file causes recovery to end whether or not the next WAL file is available.
So, some *available* transactions may be lost at failover. Such danger
will become high if the standby server has not caught up with the primary.
Attached patch fixes the above problem by adding a new trigger option
to pg_standby; the presence of this new trigger file causes recovery to
end after replaying all the available WAL files. Specifically, pg_standby
acts like 'cp' or 'ln' command while this new trigger file exists.
I've not changed any existing features, so backward-compatibility is
maintained.
Thought?
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
pgstandby_new_trigger_0325.patchapplication/octet-stream; name=pgstandby_new_trigger_0325.patchDownload
? GNUmakefile
? config.log
? config.status
? contrib/pg_standby/pg_standby
? contrib/pgbench/pgbench
? src/Makefile.global
? src/backend/postgres
? src/backend/catalog/postgres.bki
? src/backend/catalog/postgres.description
? src/backend/catalog/postgres.shdescription
? src/backend/snowball/snowball_create.sql
? src/backend/utils/probes.h
? src/backend/utils/mb/conversion_procs/conversion_create.sql
? src/bin/initdb/initdb
? src/bin/pg_config/pg_config
? src/bin/pg_controldata/pg_controldata
? src/bin/pg_ctl/pg_ctl
? src/bin/pg_dump/pg_dump
? src/bin/pg_dump/pg_dumpall
? src/bin/pg_dump/pg_restore
? src/bin/pg_resetxlog/pg_resetxlog
? src/bin/psql/psql
? src/bin/scripts/clusterdb
? src/bin/scripts/createdb
? src/bin/scripts/createlang
? src/bin/scripts/createuser
? src/bin/scripts/dropdb
? src/bin/scripts/droplang
? src/bin/scripts/dropuser
? src/bin/scripts/reindexdb
? src/bin/scripts/vacuumdb
? src/include/pg_config.h
? src/include/stamp-h
? src/interfaces/ecpg/compatlib/exports.list
? src/interfaces/ecpg/compatlib/libecpg_compat.so.3.1
? src/interfaces/ecpg/ecpglib/exports.list
? src/interfaces/ecpg/ecpglib/libecpg.so.6.1
? src/interfaces/ecpg/include/ecpg_config.h
? src/interfaces/ecpg/include/stamp-h
? src/interfaces/ecpg/pgtypeslib/exports.list
? src/interfaces/ecpg/pgtypeslib/libpgtypes.so.3.1
? src/interfaces/ecpg/preproc/ecpg
? src/interfaces/libpq/exports.list
? src/interfaces/libpq/libpq.so.5.2
? src/port/pg_config_paths.h
? src/test/regress/pg_regress
? src/test/regress/testtablespace
? src/timezone/zic
Index: contrib/pg_standby/pg_standby.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pg_standby/pg_standby.c,v
retrieving revision 1.20
diff -c -r1.20 pg_standby.c
*** contrib/pg_standby/pg_standby.c 18 Mar 2009 20:30:35 -0000 1.20
--- contrib/pg_standby/pg_standby.c 25 Mar 2009 06:26:40 -0000
***************
*** 52,65 ****
int keepfiles = 0; /* number of WAL files to keep, 0 keep all */
int maxretries = 3; /* number of retries on restore command */
bool debug = false; /* are we debugging? */
! bool triggered = false; /* have we been triggered? */
bool need_cleanup = false; /* do we need to remove files from
* archive? */
static volatile sig_atomic_t signaled = false;
char *archiveLocation; /* where to find the archive? */
- char *triggerPath; /* where to find the trigger file? */
char *xlogFilePath; /* where we are going to restore to */
char *nextWALFileName; /* the file we need to get from archive */
char *restartWALFileName; /* the file from which we can restart restore */
--- 52,64 ----
int keepfiles = 0; /* number of WAL files to keep, 0 keep all */
int maxretries = 3; /* number of retries on restore command */
bool debug = false; /* are we debugging? */
! bool triggered = false; /* have we been triggered (cancel)? */
bool need_cleanup = false; /* do we need to remove files from
* archive? */
static volatile sig_atomic_t signaled = false;
char *archiveLocation; /* where to find the archive? */
char *xlogFilePath; /* where we are going to restore to */
char *nextWALFileName; /* the file we need to get from archive */
char *restartWALFileName; /* the file from which we can restart restore */
***************
*** 69,74 ****
--- 68,92 ----
char exclusiveCleanupFileName[MAXPGPATH]; /* the file we need to
* get from archive */
+ /*
+ * Where to find the trigger file?
+ *
+ * Two types (finish and cancel) of trigger files are supported.
+ *
+ * When "finish" trigger file exists, pg_standby acts as cp or ln command itself,
+ * and recovery waits to finish until all the available WAL files are redone. It's
+ * guaranteed that no "available" commit transactions are lost. But it might take
+ * some times before finishing recovery.
+ *
+ * On the other hand, the existence of "cancel" trigger file causes recovery to
+ * end immediately even if the available WAL files remain. So, some transactions
+ * might be lost.
+ *
+ * When both of them exist, we prefer "cancel" trigger file to "finish" one.
+ */
+ char *finishTriggerPath;
+ char *cancelTriggerPath;
+
#define RESTORE_COMMAND_COPY 0
#define RESTORE_COMMAND_LINK 1
int restoreCommandType;
***************
*** 355,364 ****
* Is there a trigger file?
*/
static bool
! CheckForExternalTrigger(void)
{
int rc;
/*
* Look for a trigger file, if that option has been selected
*
--- 373,389 ----
* Is there a trigger file?
*/
static bool
! CheckForExternalTrigger(char *triggerPath, bool delete_trigger)
{
int rc;
+ if (debug)
+ {
+ if (triggerPath)
+ fprintf(stderr, " Checking for trigger file...: %s", triggerPath);
+ fflush(stderr);
+ }
+
/*
* Look for a trigger file, if that option has been selected
*
***************
*** 367,388 ****
*/
if (triggerPath && stat(triggerPath, &stat_buf) == 0)
{
! fprintf(stderr, "trigger file found\n");
fflush(stderr);
! /*
! * If trigger file found, we *must* delete it. Here's why: When
! * recovery completes, we will be asked again for the same file from
! * the archive using pg_standby so must remove trigger file so we can
! * reload file again and come up correctly.
! */
! rc = unlink(triggerPath);
! if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno));
! fflush(stderr);
! exit(rc);
}
return true;
}
--- 392,411 ----
*/
if (triggerPath && stat(triggerPath, &stat_buf) == 0)
{
! fprintf(stderr, "trigger file found: %s\n", triggerPath);
fflush(stderr);
! if (delete_trigger)
{
! rc = unlink(triggerPath);
! if (rc != 0)
! {
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno));
! fflush(stderr);
! exit(rc);
! }
}
+
return true;
}
***************
*** 450,456 ****
" (default=3)\n");
printf(" -s SLEEPTIME seconds to wait between file checks (min=1, max=60,\n"
" default=5)\n");
! printf(" -t TRIGGERFILE defines a trigger file to initiate failover (no default)\n");
printf(" -w MAXWAITTIME max seconds to wait for a file (0=no limit) (default=0)\n");
printf(" --help show this help, then exit\n");
printf(" --version output version information, then exit\n");
--- 473,480 ----
" (default=3)\n");
printf(" -s SLEEPTIME seconds to wait between file checks (min=1, max=60,\n"
" default=5)\n");
! printf(" -t CANCELTRIGGERFILE defines a trigger file to cancel recovery (no default)\n");
! printf(" -T FINISHTRIGGERFILE defines a trigger file to finish recovery (no default)\n");
printf(" -w MAXWAITTIME max seconds to wait for a file (0=no limit) (default=0)\n");
printf(" --help show this help, then exit\n");
printf(" --version output version information, then exit\n");
***************
*** 513,519 ****
(void) signal(SIGQUIT, sigquit_handler);
#endif
! while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1)
{
switch (c)
{
--- 537,543 ----
(void) signal(SIGQUIT, sigquit_handler);
#endif
! while ((c = getopt(argc, argv, "cdk:lr:s:t:T:w:")) != -1)
{
switch (c)
{
***************
*** 550,560 ****
exit(2);
}
break;
! case 't': /* Trigger file */
! triggerPath = optarg;
! if (CheckForExternalTrigger())
exit(1); /* Normal exit, with non-zero */
break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
if (maxwaittime < 0)
--- 574,587 ----
exit(2);
}
break;
! case 't': /* Cancel trigger file */
! cancelTriggerPath = optarg;
! if (CheckForExternalTrigger(cancelTriggerPath, true))
exit(1); /* Normal exit, with non-zero */
break;
+ case 'T': /* Finish trigger file */
+ finishTriggerPath = optarg;
+ break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
if (maxwaittime < 0)
***************
*** 633,639 ****
if (debug)
{
! fprintf(stderr, "\nTrigger file : %s", triggerPath ? triggerPath : "<not set>");
fprintf(stderr, "\nWaiting for WAL file : %s", nextWALFileName);
fprintf(stderr, "\nWAL file path : %s", WALFilePath);
fprintf(stderr, "\nRestoring to... : %s", xlogFilePath);
--- 660,669 ----
if (debug)
{
! fprintf(stderr, "\nCancel trigger file : %s",
! cancelTriggerPath ? cancelTriggerPath : "<not set>");
! fprintf(stderr, "\nFinish trigger file : %s",
! finishTriggerPath ? finishTriggerPath : "<not set>");
fprintf(stderr, "\nWaiting for WAL file : %s", nextWALFileName);
fprintf(stderr, "\nWAL file path : %s", WALFilePath);
fprintf(stderr, "\nRestoring to... : %s", xlogFilePath);
***************
*** 673,724 ****
}
}
! /*
! * Main wait loop
! */
! while (!CustomizableNextWALFileReady() && !triggered)
! {
! if (sleeptime <= 60)
! pg_usleep(sleeptime * 1000000L);
!
! if (signaled)
{
! triggered = true;
! if (debug)
{
! fprintf(stderr, "\nsignaled to exit\n");
! fflush(stderr);
}
! }
! else
! {
!
! if (debug)
{
! fprintf(stderr, "\nWAL file not present yet.");
! if (triggerPath)
! fprintf(stderr, " Checking for trigger file...");
! fflush(stderr);
}
! waittime += sleeptime;
!
! if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0)))
! {
! triggered = true;
! if (debug && waittime >= maxwaittime && maxwaittime > 0)
! fprintf(stderr, "\nTimed out after %d seconds\n", waittime);
! }
}
}
/*
- * Action on exit
- */
- if (triggered)
- exit(1); /* Normal exit, with non-zero */
-
- /*
* Once we have restored this file successfully we can remove some prior
* WAL files. If this restore fails we musn't remove any file because some
* of them will be requested again immediately after the failed restore,
--- 703,771 ----
}
}
! /*
! * If "finish" trigger file exists, we would skip the wait loop and try to
! * restore the log, which makes pg_standby act as cp or ln command.
! */
! if (!CheckForExternalTrigger(finishTriggerPath, false))
! {
! /*
! * Main wait loop
! */
! while (!CustomizableNextWALFileReady() && !triggered)
{
! if (sleeptime <= 60)
! pg_usleep(sleeptime * 1000000L);
!
! if (signaled)
{
! triggered = true;
! if (debug)
! {
! fprintf(stderr, "\nsignaled to exit\n");
! fflush(stderr);
! }
}
! else
{
! if (debug)
! {
! fprintf(stderr, "\nWAL file not present yet.");
! fflush(stderr);
! }
!
! waittime += sleeptime;
!
! /*
! * If "cancel" trigger file found, we *must* delete it. Here's why: When
! * recovery finishes, we will be asked again for the same file from
! * the archive using pg_standby so must remove trigger file so we can
! * reload file again and come up correctly.
! */
! if (!triggered && (CheckForExternalTrigger(cancelTriggerPath, true) ||
! (waittime >= maxwaittime && maxwaittime > 0)))
! {
! triggered = true;
! if (debug && waittime >= maxwaittime && maxwaittime > 0)
! fprintf(stderr, "\nTimed out after %d seconds\n", waittime);
! }
}
! /*
! * Action on exit
! */
! if (triggered)
! exit(1); /* Normal exit, with non-zero */
!
! /*
! * If "finish" trigger file exists, we try to restore the log soon.
! */
! if (CheckForExternalTrigger(finishTriggerPath, false))
! break;
}
}
/*
* Once we have restored this file successfully we can remove some prior
* WAL files. If this restore fails we musn't remove any file because some
* of them will be requested again immediately after the failed restore,
Index: doc/src/sgml/pgstandby.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/pgstandby.sgml,v
retrieving revision 2.7
diff -c -r2.7 pgstandby.sgml
*** doc/src/sgml/pgstandby.sgml 27 Feb 2009 09:30:21 -0000 2.7
--- doc/src/sgml/pgstandby.sgml 25 Mar 2009 06:26:40 -0000
***************
*** 174,180 ****
</entry>
</row>
<row>
! <entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
Specify a trigger file whose presence should cause recovery to end
--- 174,180 ----
</entry>
</row>
<row>
! <entry><literal>-t</> <replaceable>canceltrigger</></entry>
<entry>none</entry>
<entry>
Specify a trigger file whose presence should cause recovery to end
***************
*** 182,188 ****
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
! <filename>/tmp/pgsql.trigger.5432</>.
</entry>
</row>
<row>
--- 182,201 ----
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
! <filename>/tmp/pgsql.cancel.5442</>.
! Note that the trigger file doesn't exist after recovery.
! </entry>
! </row>
! <row>
! <entry><literal>-T</> <replaceable>finishtrigger</></entry>
! <entry>none</entry>
! <entry>
! Specify a trigger file whose presence should cause recovery to end
! after replaying all the available WAL files.
! It is recommended that you use a structured filename like <literal>-t</>.
! <literal>-T</> is ignored if the same trigger file as that of
! <literal>-t</> is specified.
! Note that the trigger file remains even after recovery.
</entry>
</row>
<row>
***************
*** 209,215 ****
<programlisting>
archive_command = 'cp %p .../archive/%f'
! restore_command = 'pg_standby -l -d -s 2 -t /tmp/pgsql.trigger.5442 .../archive %f %p %r 2>>standby.log'
</programlisting>
<para>
where the archive directory is physically located on the standby server,
--- 222,228 ----
<programlisting>
archive_command = 'cp %p .../archive/%f'
! restore_command = 'pg_standby -l -d -s 2 -t /tmp/pgsql.cancel.5442 -T /tmp/pgsql.finish.5442 .../archive %f %p %r 2>>standby.log'
</programlisting>
<para>
where the archive directory is physically located on the standby server,
***************
*** 236,242 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 249,263 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.cancel.5442</> appears,
! then do nothing even if there are the available WAL files
! </para>
! </listitem>
! <listitem>
! <para>
! stop waiting only when a trigger file called
! <filename>/tmp/pgsql.finish.5442</> appears,
! then restore the available WAL file if it exists
</para>
</listitem>
<listitem>
***************
*** 251,257 ****
<programlisting>
archive_command = 'copy %p ...\\archive\\%f'
! restore_command = 'pg_standby -d -s 5 -t C:\pgsql.trigger.5442 ...\archive %f %p %r 2>>standby.log'
</programlisting>
<para>
Note that backslashes need to be doubled in the
--- 272,278 ----
<programlisting>
archive_command = 'copy %p ...\\archive\\%f'
! restore_command = 'pg_standby -d -s 5 -t C:\pgsql.cancel.5442 -T C:\pgsql.finish.5442 ...\archive %f %p %r 2>>standby.log'
</programlisting>
<para>
Note that backslashes need to be doubled in the
***************
*** 277,283 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 298,312 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.cancel.5442</> appears,
! then do nothing even if there are the available WAL files
! </para>
! </listitem>
! <listitem>
! <para>
! stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears,
! then restore the available WAL file if it exists
</para>
</listitem>
<listitem>
On Wed, Mar 25, 2009 at 7:29 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Attached patch fixes the above problem by adding a new trigger option
to pg_standby; the presence of this new trigger file causes recovery to
end after replaying all the available WAL files.
Shouldn't it be the default? It seems like the most expected behaviour to me.
--
Guillaume
Hi,
Thanks for the comment.
On Wed, Mar 25, 2009 at 3:50 PM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:
On Wed, Mar 25, 2009 at 7:29 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Attached patch fixes the above problem by adding a new trigger option
to pg_standby; the presence of this new trigger file causes recovery to
end after replaying all the available WAL files.Shouldn't it be the default? It seems like the most expected behaviour to me.
Yeah, I agree... but there may be scripts for warm-standby based on
the existing default behavior. So, I didn't make a new trigger the default.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Wed, Mar 25, 2009 at 9:44 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Yeah, I agree... but there may be scripts for warm-standby based on
the existing default behavior. So, I didn't make a new trigger the default.
I don't use pg_standby personnaly but I admit I'm quite surprised by
the current behaviour. I'm pretty sure a lot of the current users
would be surprised too.
--
Guillaume
Hi,
On Wed, Mar 25, 2009 at 5:55 PM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:
On Wed, Mar 25, 2009 at 9:44 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Yeah, I agree... but there may be scripts for warm-standby based on
the existing default behavior. So, I didn't make a new trigger the default.I don't use pg_standby personnaly but I admit I'm quite surprised by
the current behaviour. I'm pretty sure a lot of the current users
would be surprised too.
The current behavior is documented as follows, so it may be
taken for granted by some users. I think that we shouldn't
ignore such users.
---------------
Specify a trigger file whose presence should cause recovery to
end whether or not the next WAL file is available.
---------------
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Guillaume Smet <guillaume.smet@gmail.com> wrote:
On Wed, Mar 25, 2009 at 9:44 AM, Fujii Masao <masao.fujii@gmail.com>
wrote:
Yeah, I agree... but there may be scripts for warm-standby based on
the existing default behavior. So, I didn't make a new trigger the
default.
I don't use pg_standby personnaly but I admit I'm quite surprised by
the current behaviour. I'm pretty sure a lot of the current users
would be surprised too.
I find it hard to imagine a use case for the existing default
behavior.
-Kevin
On Wed, Mar 25, 2009 at 2:59 PM, Kevin Grittner
<Kevin.Grittner@wicourts.gov> wrote:
I find it hard to imagine a use case for the existing default
behavior.
I thought a bit about it and I think it can be useful when your
priority is the availability of the service and you don't consider a
data loss that important: even if you have a lot of WALs segments to
replay, you may want to have your service up immediately in case of a
major problem.
Keeping it is a good idea IMHO but I don't think it should be the default.
--
Guillaume
Hi,
On Thu, Mar 26, 2009 at 12:48 AM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:
On Wed, Mar 25, 2009 at 2:59 PM, Kevin Grittner
<Kevin.Grittner@wicourts.gov> wrote:I find it hard to imagine a use case for the existing default
behavior.I thought a bit about it and I think it can be useful when your
priority is the availability of the service and you don't consider a
data loss that important: even if you have a lot of WALs segments to
replay, you may want to have your service up immediately in case of a
major problem.
Yes, I also think that this is likely use case.
Keeping it is a good idea IMHO but I don't think it should be the default.
What does "the default" mean? You mean that new trigger should use
the existing trigger option character (-t)?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Fujii Masao wrote:
On Thu, Mar 26, 2009 at 12:48 AM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:On Wed, Mar 25, 2009 at 2:59 PM, Kevin Grittner
<Kevin.Grittner@wicourts.gov> wrote:I find it hard to imagine a use case for the existing default
behavior.I thought a bit about it and I think it can be useful when your
priority is the availability of the service and you don't consider a
data loss that important: even if you have a lot of WALs segments to
replay, you may want to have your service up immediately in case of a
major problem.Yes, I also think that this is likely use case.
Keeping it is a good idea IMHO but I don't think it should be the default.
What does "the default" mean? You mean that new trigger should use
the existing trigger option character (-t)?
The existing behavior doesn't seem very useful to me either. Assuming
there is a use case though, we probably need to support both at the same
time, perhaps using different trigger files. If there's a use case for
both, conceivably someone will want to sometimes trigger the failover
immediately and sometimes after all WAL segments have been replayed.
Whatever we do, the signaling method to trigger failover should behave
the same.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Thu, Mar 26, 2009 at 2:51 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
What does "the default" mean? You mean that new trigger should use
the existing trigger option character (-t)?
Yes, that's my point.
I understand it seems weird to switch the options but I'm pretty sure
a lot of persons currently using -t would be surprised by the current
behaviour. Moreover playing all the remaining WALs before starting up
should be the most natural option when people are looking in the help.
That said, it would be nice to hear from people really using
pg_standby to know if they understand how it works now and if it's
what they intended when they set it up.
--
Guillaume
Hi,
Guillaume Smet wrote:
On Thu, Mar 26, 2009 at 2:51 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
What does "the default" mean? You mean that new trigger should use
the existing trigger option character (-t)?Yes, that's my point.
I understand it seems weird to switch the options but I'm pretty sure
a lot of persons currently using -t would be surprised by the current
behaviour. Moreover playing all the remaining WALs before starting up
should be the most natural option when people are looking in the help.That said, it would be nice to hear from people really using
pg_standby to know if they understand how it works now and if it's
what they intended when they set it up.
My fault not RTFM well enough, but I was surprised finding out that -t
is working like that.
+1 for me to switch -t to the new behaviour.
Cheers
--
Matteo Beccati
OpenX - http://www.openx.org
On Thu, 2009-03-26 at 08:32 +0100, Guillaume Smet wrote:
On Thu, Mar 26, 2009 at 2:51 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
What does "the default" mean? You mean that new trigger should use
the existing trigger option character (-t)?Yes, that's my point.
I understand it seems weird to switch the options but I'm pretty sure
a lot of persons currently using -t would be surprised by the current
behaviour. Moreover playing all the remaining WALs before starting up
should be the most natural option when people are looking in the help.
If the standby has fallen behind then waiting for it to catch up might
take hours to failover if it waits for all files. If you haven't been
monitoring it correctly, you have no clue. That is also a surprising
thing, so let's not jump from one surprising thing into the arms of
another.
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover. This then forces people to read and
understand the difference between the two behaviours so they can make an
informed choice of how they would like to act at this critical point in
time. It is justifiable because there is no single thing called a
trigger file any longer and the concept will lead to pain.
Earlier, we discussed having a single trigger file that contains an
option rather than two distinct trigger files. That design is better
because it allows the user to choose at failover time, rather than
making a binding decision at config time. That solution would be the
ideal one, IMHO, because it gives user more choice - and would allow us
to keep the -t option meaningfully. In that case the default should be
patience.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi Simon.
On Thu, Mar 26, 2009 at 11:50 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
Earlier, we discussed having a single trigger file that contains an
option rather than two distinct trigger files. That design is better
because it allows the user to choose at failover time, rather than
making a binding decision at config time. That solution would be the
ideal one, IMHO, because it gives user more choice - and would allow us
to keep the -t option meaningfully. In that case the default should be
patience.
Or you can define both files in your command line to have the choice.
I like the idea of removing -t and adding 2 new options so that people
are warned about the intended behavior.
Anyway, I don't have a strong opinion about how we should fix it as I
don't use pg_standby personnally, just that we should. The two options
you mention have their own merits.
--
Guillaume
Hi,
On Thu, Mar 26, 2009 at 8:54 PM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:
Hi Simon.
On Thu, Mar 26, 2009 at 11:50 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
Earlier, we discussed having a single trigger file that contains an
option rather than two distinct trigger files. That design is better
because it allows the user to choose at failover time, rather than
making a binding decision at config time. That solution would be the
ideal one, IMHO, because it gives user more choice - and would allow us
to keep the -t option meaningfully. In that case the default should be
patience.Or you can define both files in your command line to have the choice.
Personally I like this.
I like the idea of removing -t and adding 2 new options so that people
are warned about the intended behavior.
OK, I'll change the patch as Simon suggested; removing -t and adding
two new options: -f = fast failover (existing behavior), -p patient failover.
Also I'll default the patient failover, so it's performed when the signal
(SIGINT or SIGUSR1) is received.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Fri, Mar 27, 2009 at 3:38 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
OK, I'll change the patch as Simon suggested; removing -t and adding
two new options: -f = fast failover (existing behavior), -p patient failover.
Also I'll default the patient failover, so it's performed when the signal
(SIGINT or SIGUSR1) is received.
I'm wondering if we should consider backpatching this one. Even if the
feature works as advertised in the documentation.
It's a very surprising behaviour and I'm pretty sure someone will
shoot himself in the foot with it, if not already done.
Considering backpatching might change the way we want to fix it.
--
Guillaume
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.
-m smart|fast|immediate :-)
On Fri, 2009-03-27 at 13:56 +0200, Peter Eisentraut wrote:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.-m smart|fast|immediate :-)
Yes, a better suggestion.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
On Fri, Mar 27, 2009 at 12:56 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.-m smart|fast|immediate :-)
The advantage of having 2 options (or the ability to put a string
value in the trigger file) is that you can choose the behaviour when
you need to trigger it (you just have to use the 2 options with 2
different filenames). I don't think it's the case with your proposal.
--
Guillaume
Fujii Masao wrote:
On Thu, Mar 26, 2009 at 8:54 PM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:On Thu, Mar 26, 2009 at 11:50 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
I like the idea of removing -t and adding 2 new options so that people
are warned about the intended behavior.OK, I'll change the patch as Simon suggested; removing -t and adding
two new options: -f = fast failover (existing behavior), -p patient failover.
Also I'll default the patient failover, so it's performed when the signal
(SIGINT or SIGUSR1) is received.
Uh oh, that's going to be quite tricky with signals. Remember that
pg_standby is called for each file. A trigger file persists until it's
deleted, but a signal will only be received by the pg_standby instance
that happens to be running at the time.
Makes me wonder if the trigger pg_standby with signals is reliable to
begin with. What if the backend is just processing a file when the
signal is fired, and there's no pg_standby process running at the moment
to receive it? Seems like the signaler needs to loop until it has
successfully delivered the signal to a pg_standby process, which seems
pretty ugly.
Given all the recent trouble with signals, and the fact that it's
undocumented, perhaps we should just rip out the signaling support from
pg_standby.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Fri, 2009-03-27 at 13:19 +0100, Guillaume Smet wrote:
On Fri, Mar 27, 2009 at 12:56 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.-m smart|fast|immediate :-)
The advantage of having 2 options (or the ability to put a string
value in the trigger file) is that you can choose the behaviour when
you need to trigger it (you just have to use the 2 options with 2
different filenames). I don't think it's the case with your proposal.
Yes, sorry. I meant we should use the naming Peter suggests.
So we would have two triggers, but call them fast and smart, rather than
fast and patient.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Peter Eisentraut <peter_e@gmx.net> writes:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.
-m smart|fast|immediate :-)
+1 for using a "-m something" type of syntax instead of having to try to
pick single-letter switches that are mnemonic for the different cases.
But -1 to those particular mode names --- I think it will invite
confusion with pg_ctl's behavior.
regards, tom lane
On Fri, 2009-03-27 at 10:25 -0400, Tom Lane wrote:
Peter Eisentraut <peter_e@gmx.net> writes:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.-m smart|fast|immediate :-)
+1 for using a "-m something" type of syntax instead of having to try to
pick single-letter switches that are mnemonic for the different cases.
But -1 to those particular mode names --- I think it will invite
confusion with pg_ctl's behavior.
The choice is between
* one parameter with the option being given as text within trigger file
* two parameters naming different types of trigger file
I don't mind which, as long as it is one of those two, unless there is a
third way to specify things so that user has control at failover time. A
single -m option would hardcode that decision ahead of time, which is
undesirable behaviour, hence the additional complexity being discussed.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
On Fri, Mar 27, 2009 at 9:49 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
Uh oh, that's going to be quite tricky with signals. Remember that
pg_standby is called for each file. A trigger file persists until it's
deleted, but a signal will only be received by the pg_standby instance that
happens to be running at the time.
You are right!
Makes me wonder if the trigger pg_standby with signals is reliable to begin
with. What if the backend is just processing a file when the signal is
fired, and there's no pg_standby process running at the moment to receive
it? Seems like the signaler needs to loop until it has successfully
delivered the signal to a pg_standby process, which seems pretty ugly.Given all the recent trouble with signals, and the fact that it's
undocumented, perhaps we should just rip out the signaling support from
pg_standby.
So far, to be frank, I was not sure why the trigger by the signal is necessary
for pg_standby. But, now, I think that it's useful when the user has forgotten
to specify the trigger file. In this case, without the signaling
support, there is
no way to do failover in a short time; probably, the user has to do shutdown
and restart a recovery from the last restart point, which would take time.
So, I'd like to leave the signaling support as a safeguard.
As you pointed out, the "smart" failover by signal would be very tricky. So,
maybe we should not change the existing behavior of pg_standby when
the signal is received.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Thu, Mar 26, 2009 at 4:54 AM, Guillaume Smet <guillaume.smet@gmail.com>wrote:
Hi Simon.
On Thu, Mar 26, 2009 at 11:50 AM, Simon Riggs <simon@2ndquadrant.com>
wrote:Earlier, we discussed having a single trigger file that contains an
option rather than two distinct trigger files. That design is better
because it allows the user to choose at failover time, rather than
making a binding decision at config time. That solution would be the
ideal one, IMHO, because it gives user more choice - and would allow us
to keep the -t option meaningfully. In that case the default should be
patience.Or you can define both files in your command line to have the choice.
I like the idea of removing -t and adding 2 new options so that people
are warned about the intended behavior.Anyway, I don't have a strong opinion about how we should fix it as I
don't use pg_standby personnally, just that we should. The two options
you mention have their own merits.
For the record, I have been a user of pg_standby in the past, and never
realized this behavioural detail; probably because we never had a huge lag
at the slave.
From implementation perspective, I'd vote against any new options. That'd
just make it harder for backporting to older branches. I like the idea that
contents of the trigger file determines the mode of operation.
So, we can keep the existing behaviour where an empty trigger file waits for
all WAL files to be applied, hence no surprises for existing users. With
some strong wordings in the docs, we emit a log message like
'Trigger file found; Performing patient recovery'
that makes the user read the docs and understand the difference, if he has
not already done so. And if there are some contents in the trigger file,
then the behaviour depends on that.
If we do not want to go to lengths of reading the file contents, the
behaviour can depend on the suffix of the file.
pg_standby -t /tmp/mytrigger ... -- lest assume this is the restore_command
if( exists /tmp/mytrigger ) do default patient recovery with appropriate
messages in log
if( exists /tmp/mytrigger.fast ) do stop the recovery immediately with a
message in log
if( exists /tmp/mytrigger.normal ) do the patient recovery with messages in
log
Best regards,
--
gurjeet[.singh]@EnterpriseDB.com
singh.gurjeet@{ gmail | hotmail | indiatimes | yahoo }.com
EnterpriseDB http://www.enterprisedb.com
Mail sent from my BlackLaptop device
Hi,
On Fri, Mar 27, 2009 at 11:36 PM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Fri, 2009-03-27 at 10:25 -0400, Tom Lane wrote:
Peter Eisentraut <peter_e@gmx.net> writes:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.-m smart|fast|immediate :-)
+1 for using a "-m something" type of syntax instead of having to try to
pick single-letter switches that are mnemonic for the different cases.
But -1 to those particular mode names --- I think it will invite
confusion with pg_ctl's behavior.The choice is between
* one parameter with the option being given as text within trigger file
* two parameters naming different types of trigger file
I don't mind which, as long as it is one of those two, unless there is a
third way to specify things so that user has control at failover time. A
single -m option would hardcode that decision ahead of time, which is
undesirable behaviour, hence the additional complexity being discussed.
Thanks for the clarification.
I'd like to choose the former because it's more flexible when new
trigger action is added to pg_standby in the future. And, as Gurjeet
says, it's more friendly to do smart failover (end recovery after all
the available WAL are applied) when an empty trigger file exists.
I'll change the patch as above. Comments?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Hi,
On Wed, Apr 1, 2009 at 11:01 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Hi,
On Fri, Mar 27, 2009 at 11:36 PM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Fri, 2009-03-27 at 10:25 -0400, Tom Lane wrote:
Peter Eisentraut <peter_e@gmx.net> writes:
Simon Riggs wrote:
If we go with this, I would suggest we make *neither* the default by
removing -t, and adopting two new options: something like -f == fast
failover, -p == patient failover.-m smart|fast|immediate :-)
+1 for using a "-m something" type of syntax instead of having to try to
pick single-letter switches that are mnemonic for the different cases.
But -1 to those particular mode names --- I think it will invite
confusion with pg_ctl's behavior.The choice is between
* one parameter with the option being given as text within trigger file
* two parameters naming different types of trigger file
I don't mind which, as long as it is one of those two, unless there is a
third way to specify things so that user has control at failover time. A
single -m option would hardcode that decision ahead of time, which is
undesirable behaviour, hence the additional complexity being discussed.Thanks for the clarification.
I'd like to choose the former because it's more flexible when new
trigger action is added to pg_standby in the future. And, as Gurjeet
says, it's more friendly to do smart failover (end recovery after all
the available WAL are applied) when an empty trigger file exists.
I'll change the patch as above. Comments?
Here is the patch;
- Smart failover is chosen if the trigger file labeled "smart" or
an empty one exists.
- Fast failover is chosen if the trigger file labeled "fast" exists,
the signal (SIGUSR1 or SIGINT) is received or the wait timeout
happens.
If you notice anything, please feel free to comment.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
pgstandby_new_trigger_0403.patchtext/x-patch; charset=US-ASCII; name=pgstandby_new_trigger_0403.patchDownload
? GNUmakefile
? config.log
? config.status
? contrib/pg_standby/pg_standby
? contrib/pgbench/pgbench
? doc/src/sgml/HTML.index
? doc/src/sgml/HTML.index.start
? doc/src/sgml/LEGALNOTICE.html
? doc/src/sgml/acronyms.html
? doc/src/sgml/admin.html
? doc/src/sgml/adminpack.html
? doc/src/sgml/anoncvs.html
? doc/src/sgml/app-clusterdb.html
? doc/src/sgml/app-createdb.html
? doc/src/sgml/app-createlang.html
? doc/src/sgml/app-createuser.html
? doc/src/sgml/app-dropdb.html
? doc/src/sgml/app-droplang.html
? doc/src/sgml/app-dropuser.html
? doc/src/sgml/app-ecpg.html
? doc/src/sgml/app-initdb.html
? doc/src/sgml/app-pg-ctl.html
? doc/src/sgml/app-pg-dumpall.html
? doc/src/sgml/app-pgconfig.html
? doc/src/sgml/app-pgcontroldata.html
? doc/src/sgml/app-pgdump.html
? doc/src/sgml/app-pgresetxlog.html
? doc/src/sgml/app-pgrestore.html
? doc/src/sgml/app-postgres.html
? doc/src/sgml/app-postmaster.html
? doc/src/sgml/app-psql.html
? doc/src/sgml/app-reindexdb.html
? doc/src/sgml/app-vacuumdb.html
? doc/src/sgml/appendixes.html
? doc/src/sgml/applevel-consistency.html
? doc/src/sgml/arrays.html
? doc/src/sgml/auth-methods.html
? doc/src/sgml/auth-pg-hba-conf.html
? doc/src/sgml/auth-username-maps.html
? doc/src/sgml/auto-explain.html
? doc/src/sgml/backup-dump.html
? doc/src/sgml/backup-file.html
? doc/src/sgml/backup.html
? doc/src/sgml/biblio.html
? doc/src/sgml/bki-commands.html
? doc/src/sgml/bki-example.html
? doc/src/sgml/bki-format.html
? doc/src/sgml/bki-structure.html
? doc/src/sgml/bki.html
? doc/src/sgml/bookindex.html
? doc/src/sgml/bookindex.sgml
? doc/src/sgml/btree-gin.html
? doc/src/sgml/btree-gist.html
? doc/src/sgml/bug-reporting.html
? doc/src/sgml/catalog-pg-aggregate.html
? doc/src/sgml/catalog-pg-am.html
? doc/src/sgml/catalog-pg-amop.html
? doc/src/sgml/catalog-pg-amproc.html
? doc/src/sgml/catalog-pg-attrdef.html
? doc/src/sgml/catalog-pg-attribute.html
? doc/src/sgml/catalog-pg-auth-members.html
? doc/src/sgml/catalog-pg-authid.html
? doc/src/sgml/catalog-pg-cast.html
? doc/src/sgml/catalog-pg-class.html
? doc/src/sgml/catalog-pg-constraint.html
? doc/src/sgml/catalog-pg-conversion.html
? doc/src/sgml/catalog-pg-database.html
? doc/src/sgml/catalog-pg-depend.html
? doc/src/sgml/catalog-pg-description.html
? doc/src/sgml/catalog-pg-enum.html
? doc/src/sgml/catalog-pg-foreign-data-wrapper.html
? doc/src/sgml/catalog-pg-foreign-server.html
? doc/src/sgml/catalog-pg-index.html
? doc/src/sgml/catalog-pg-inherits.html
? doc/src/sgml/catalog-pg-language.html
? doc/src/sgml/catalog-pg-largeobject.html
? doc/src/sgml/catalog-pg-listener.html
? doc/src/sgml/catalog-pg-namespace.html
? doc/src/sgml/catalog-pg-opclass.html
? doc/src/sgml/catalog-pg-operator.html
? doc/src/sgml/catalog-pg-opfamily.html
? doc/src/sgml/catalog-pg-pltemplate.html
? doc/src/sgml/catalog-pg-proc.html
? doc/src/sgml/catalog-pg-rewrite.html
? doc/src/sgml/catalog-pg-shdepend.html
? doc/src/sgml/catalog-pg-shdescription.html
? doc/src/sgml/catalog-pg-statistic.html
? doc/src/sgml/catalog-pg-tablespace.html
? doc/src/sgml/catalog-pg-trigger.html
? doc/src/sgml/catalog-pg-ts-config-map.html
? doc/src/sgml/catalog-pg-ts-config.html
? doc/src/sgml/catalog-pg-ts-dict.html
? doc/src/sgml/catalog-pg-ts-parser.html
? doc/src/sgml/catalog-pg-ts-template.html
? doc/src/sgml/catalog-pg-type.html
? doc/src/sgml/catalog-pg-user-mapping.html
? doc/src/sgml/catalogs-overview.html
? doc/src/sgml/catalogs.html
? doc/src/sgml/charset.html
? doc/src/sgml/chkpass.html
? doc/src/sgml/citext.html
? doc/src/sgml/client-authentication-problems.html
? doc/src/sgml/client-authentication.html
? doc/src/sgml/client-interfaces.html
? doc/src/sgml/config-setting.html
? doc/src/sgml/connect-estab.html
? doc/src/sgml/continuous-archiving.html
? doc/src/sgml/contrib-dblink-build-sql-delete.html
? doc/src/sgml/contrib-dblink-build-sql-insert.html
? doc/src/sgml/contrib-dblink-build-sql-update.html
? doc/src/sgml/contrib-dblink-cancel-query.html
? doc/src/sgml/contrib-dblink-close.html
? doc/src/sgml/contrib-dblink-connect-u.html
? doc/src/sgml/contrib-dblink-connect.html
? doc/src/sgml/contrib-dblink-disconnect.html
? doc/src/sgml/contrib-dblink-error-message.html
? doc/src/sgml/contrib-dblink-exec.html
? doc/src/sgml/contrib-dblink-fetch.html
? doc/src/sgml/contrib-dblink-get-connections.html
? doc/src/sgml/contrib-dblink-get-pkey.html
? doc/src/sgml/contrib-dblink-get-result.html
? doc/src/sgml/contrib-dblink-is-busy.html
? doc/src/sgml/contrib-dblink-open.html
? doc/src/sgml/contrib-dblink-send-query.html
? doc/src/sgml/contrib-dblink.html
? doc/src/sgml/contrib-spi.html
? doc/src/sgml/contrib.html
? doc/src/sgml/creating-cluster.html
? doc/src/sgml/cube.html
? doc/src/sgml/cvs-tree.html
? doc/src/sgml/cvs.html
? doc/src/sgml/cvsup.html
? doc/src/sgml/database-roles.html
? doc/src/sgml/datatype-binary.html
? doc/src/sgml/datatype-bit.html
? doc/src/sgml/datatype-boolean.html
? doc/src/sgml/datatype-character.html
? doc/src/sgml/datatype-datetime.html
? doc/src/sgml/datatype-enum.html
? doc/src/sgml/datatype-geometric.html
? doc/src/sgml/datatype-money.html
? doc/src/sgml/datatype-net-types.html
? doc/src/sgml/datatype-numeric.html
? doc/src/sgml/datatype-oid.html
? doc/src/sgml/datatype-pseudo.html
? doc/src/sgml/datatype-textsearch.html
? doc/src/sgml/datatype-uuid.html
? doc/src/sgml/datatype-xml.html
? doc/src/sgml/datatype.html
? doc/src/sgml/datetime-appendix.html
? doc/src/sgml/datetime-config-files.html
? doc/src/sgml/datetime-input-rules.html
? doc/src/sgml/datetime-keywords.html
? doc/src/sgml/datetime-units-history.html
? doc/src/sgml/dblink.html
? doc/src/sgml/ddl-alter.html
? doc/src/sgml/ddl-basics.html
? doc/src/sgml/ddl-constraints.html
? doc/src/sgml/ddl-default.html
? doc/src/sgml/ddl-depend.html
? doc/src/sgml/ddl-inherit.html
? doc/src/sgml/ddl-others.html
? doc/src/sgml/ddl-partitioning.html
? doc/src/sgml/ddl-priv.html
? doc/src/sgml/ddl-schemas.html
? doc/src/sgml/ddl-system-columns.html
? doc/src/sgml/ddl.html
? doc/src/sgml/dict-int.html
? doc/src/sgml/dict-xsyn.html
? doc/src/sgml/disk-full.html
? doc/src/sgml/disk-usage.html
? doc/src/sgml/diskusage.html
? doc/src/sgml/dml-delete.html
? doc/src/sgml/dml-insert.html
? doc/src/sgml/dml-update.html
? doc/src/sgml/dml.html
? doc/src/sgml/docguide-authoring.html
? doc/src/sgml/docguide-build.html
? doc/src/sgml/docguide-docbook.html
? doc/src/sgml/docguide-style.html
? doc/src/sgml/docguide-toolsets.html
? doc/src/sgml/docguide.html
? doc/src/sgml/dynamic-trace.html
? doc/src/sgml/earthdistance.html
? doc/src/sgml/ecpg-commands.html
? doc/src/sgml/ecpg-concept.html
? doc/src/sgml/ecpg-connect.html
? doc/src/sgml/ecpg-descriptors.html
? doc/src/sgml/ecpg-develop.html
? doc/src/sgml/ecpg-disconnect.html
? doc/src/sgml/ecpg-dynamic.html
? doc/src/sgml/ecpg-errors.html
? doc/src/sgml/ecpg-informix-compat.html
? doc/src/sgml/ecpg-library.html
? doc/src/sgml/ecpg-pgtypes.html
? doc/src/sgml/ecpg-preproc.html
? doc/src/sgml/ecpg-process.html
? doc/src/sgml/ecpg-set-connection.html
? doc/src/sgml/ecpg-variables.html
? doc/src/sgml/ecpg.html
? doc/src/sgml/encryption-options.html
? doc/src/sgml/errcodes-appendix.html
? doc/src/sgml/error-message-reporting.html
? doc/src/sgml/error-style-guide.html
? doc/src/sgml/executor.html
? doc/src/sgml/explicit-joins.html
? doc/src/sgml/explicit-locking.html
? doc/src/sgml/extend-how.html
? doc/src/sgml/extend-type-system.html
? doc/src/sgml/extend.html
? doc/src/sgml/external-extensions.html
? doc/src/sgml/external-interfaces.html
? doc/src/sgml/external-pl.html
? doc/src/sgml/external-projects.html
? doc/src/sgml/features-sql-standard.html
? doc/src/sgml/features-supported.sgml
? doc/src/sgml/features-unsupported.sgml
? doc/src/sgml/features.html
? doc/src/sgml/functions-admin.html
? doc/src/sgml/functions-aggregate.html
? doc/src/sgml/functions-array.html
? doc/src/sgml/functions-binarystring.html
? doc/src/sgml/functions-bitstring.html
? doc/src/sgml/functions-comparison.html
? doc/src/sgml/functions-comparisons.html
? doc/src/sgml/functions-conditional.html
? doc/src/sgml/functions-datetime.html
? doc/src/sgml/functions-enum.html
? doc/src/sgml/functions-formatting.html
? doc/src/sgml/functions-geometry.html
? doc/src/sgml/functions-info.html
? doc/src/sgml/functions-logical.html
? doc/src/sgml/functions-matching.html
? doc/src/sgml/functions-math.html
? doc/src/sgml/functions-net.html
? doc/src/sgml/functions-sequence.html
? doc/src/sgml/functions-srf.html
? doc/src/sgml/functions-string.html
? doc/src/sgml/functions-subquery.html
? doc/src/sgml/functions-textsearch.html
? doc/src/sgml/functions-trigger.html
? doc/src/sgml/functions-window.html
? doc/src/sgml/functions-xml.html
? doc/src/sgml/functions.html
? doc/src/sgml/fuzzystrmatch.html
? doc/src/sgml/geqo-biblio.html
? doc/src/sgml/geqo-intro.html
? doc/src/sgml/geqo-intro2.html
? doc/src/sgml/geqo-pg-intro.html
? doc/src/sgml/geqo.html
? doc/src/sgml/gin-examples.html
? doc/src/sgml/gin-extensibility.html
? doc/src/sgml/gin-implementation.html
? doc/src/sgml/gin-intro.html
? doc/src/sgml/gin-limit.html
? doc/src/sgml/gin-tips.html
? doc/src/sgml/gin.html
? doc/src/sgml/gist-examples.html
? doc/src/sgml/gist-extensibility.html
? doc/src/sgml/gist-implementation.html
? doc/src/sgml/gist-intro.html
? doc/src/sgml/gist-recovery.html
? doc/src/sgml/gist.html
? doc/src/sgml/high-availability.html
? doc/src/sgml/history.html
? doc/src/sgml/hstore.html
? doc/src/sgml/index-catalog.html
? doc/src/sgml/index-cost-estimation.html
? doc/src/sgml/index-functions.html
? doc/src/sgml/index-locking.html
? doc/src/sgml/index-scanning.html
? doc/src/sgml/index-unique-checks.html
? doc/src/sgml/index.html
? doc/src/sgml/indexam.html
? doc/src/sgml/indexes-bitmap-scans.html
? doc/src/sgml/indexes-examine.html
? doc/src/sgml/indexes-expressional.html
? doc/src/sgml/indexes-intro.html
? doc/src/sgml/indexes-multicolumn.html
? doc/src/sgml/indexes-opclass.html
? doc/src/sgml/indexes-ordering.html
? doc/src/sgml/indexes-partial.html
? doc/src/sgml/indexes-types.html
? doc/src/sgml/indexes-unique.html
? doc/src/sgml/indexes.html
? doc/src/sgml/information-schema.html
? doc/src/sgml/infoschema-administrable-role-authorizations.html
? doc/src/sgml/infoschema-applicable-roles.html
? doc/src/sgml/infoschema-attributes.html
? doc/src/sgml/infoschema-check-constraint-routine-usage.html
? doc/src/sgml/infoschema-check-constraints.html
? doc/src/sgml/infoschema-column-domain-usage.html
? doc/src/sgml/infoschema-column-privileges.html
? doc/src/sgml/infoschema-column-udt-usage.html
? doc/src/sgml/infoschema-columns.html
? doc/src/sgml/infoschema-constraint-column-usage.html
? doc/src/sgml/infoschema-constraint-table-usage.html
? doc/src/sgml/infoschema-data-type-privileges.html
? doc/src/sgml/infoschema-datatypes.html
? doc/src/sgml/infoschema-domain-constraints.html
? doc/src/sgml/infoschema-domain-udt-usage.html
? doc/src/sgml/infoschema-domains.html
? doc/src/sgml/infoschema-element-types.html
? doc/src/sgml/infoschema-enabled-roles.html
? doc/src/sgml/infoschema-foreign-data-wrapper-options.html
? doc/src/sgml/infoschema-foreign-data-wrappers.html
? doc/src/sgml/infoschema-foreign-server-options.html
? doc/src/sgml/infoschema-foreign-servers.html
? doc/src/sgml/infoschema-information-schema-catalog-name.html
? doc/src/sgml/infoschema-key-column-usage.html
? doc/src/sgml/infoschema-parameters.html
? doc/src/sgml/infoschema-referential-constraints.html
? doc/src/sgml/infoschema-role-column-grants.html
? doc/src/sgml/infoschema-role-routine-grants.html
? doc/src/sgml/infoschema-role-table-grants.html
? doc/src/sgml/infoschema-role-usage-grants.html
? doc/src/sgml/infoschema-routine-privileges.html
? doc/src/sgml/infoschema-routines.html
? doc/src/sgml/infoschema-schema.html
? doc/src/sgml/infoschema-schemata.html
? doc/src/sgml/infoschema-sequences.html
? doc/src/sgml/infoschema-sql-features.html
? doc/src/sgml/infoschema-sql-implementation-info.html
? doc/src/sgml/infoschema-sql-languages.html
? doc/src/sgml/infoschema-sql-packages.html
? doc/src/sgml/infoschema-sql-parts.html
? doc/src/sgml/infoschema-sql-sizing-profiles.html
? doc/src/sgml/infoschema-sql-sizing.html
? doc/src/sgml/infoschema-table-constraints.html
? doc/src/sgml/infoschema-table-privileges.html
? doc/src/sgml/infoschema-tables.html
? doc/src/sgml/infoschema-triggers.html
? doc/src/sgml/infoschema-usage-privileges.html
? doc/src/sgml/infoschema-user-mapping-options.html
? doc/src/sgml/infoschema-user-mappings.html
? doc/src/sgml/infoschema-view-column-usage.html
? doc/src/sgml/infoschema-view-routine-usage.html
? doc/src/sgml/infoschema-view-table-usage.html
? doc/src/sgml/infoschema-views.html
? doc/src/sgml/install-getsource.html
? doc/src/sgml/install-post.html
? doc/src/sgml/install-procedure.html
? doc/src/sgml/install-requirements.html
? doc/src/sgml/install-short.html
? doc/src/sgml/install-upgrading.html
? doc/src/sgml/install-win32-full.html
? doc/src/sgml/install-win32-libpq.html
? doc/src/sgml/install-win32.html
? doc/src/sgml/installation-platform-notes.html
? doc/src/sgml/installation.html
? doc/src/sgml/intagg.html
? doc/src/sgml/intarray.html
? doc/src/sgml/internals.html
? doc/src/sgml/intro-whatis.html
? doc/src/sgml/isn.html
? doc/src/sgml/kernel-resources.html
? doc/src/sgml/largeobjects.html
? doc/src/sgml/libpq-async.html
? doc/src/sgml/libpq-build.html
? doc/src/sgml/libpq-cancel.html
? doc/src/sgml/libpq-connect.html
? doc/src/sgml/libpq-control.html
? doc/src/sgml/libpq-copy.html
? doc/src/sgml/libpq-envars.html
? doc/src/sgml/libpq-events.html
? doc/src/sgml/libpq-example.html
? doc/src/sgml/libpq-exec.html
? doc/src/sgml/libpq-fastpath.html
? doc/src/sgml/libpq-ldap.html
? doc/src/sgml/libpq-misc.html
? doc/src/sgml/libpq-notice-processing.html
? doc/src/sgml/libpq-notify.html
? doc/src/sgml/libpq-pgpass.html
? doc/src/sgml/libpq-pgservice.html
? doc/src/sgml/libpq-ssl.html
? doc/src/sgml/libpq-status.html
? doc/src/sgml/libpq-threading.html
? doc/src/sgml/libpq.html
? doc/src/sgml/lo-examplesect.html
? doc/src/sgml/lo-funcs.html
? doc/src/sgml/lo-implementation.html
? doc/src/sgml/lo-interfaces.html
? doc/src/sgml/lo-intro.html
? doc/src/sgml/lo.html
? doc/src/sgml/locale.html
? doc/src/sgml/locking-indexes.html
? doc/src/sgml/logfile-maintenance.html
? doc/src/sgml/ltree.html
? doc/src/sgml/maintenance.html
? doc/src/sgml/manage-ag-config.html
? doc/src/sgml/manage-ag-createdb.html
? doc/src/sgml/manage-ag-dropdb.html
? doc/src/sgml/manage-ag-overview.html
? doc/src/sgml/manage-ag-tablespaces.html
? doc/src/sgml/manage-ag-templatedbs.html
? doc/src/sgml/managing-databases.html
? doc/src/sgml/migration.html
? doc/src/sgml/monitoring-locks.html
? doc/src/sgml/monitoring-ps.html
? doc/src/sgml/monitoring-stats.html
? doc/src/sgml/monitoring.html
? doc/src/sgml/multibyte.html
? doc/src/sgml/mvcc-intro.html
? doc/src/sgml/mvcc.html
? doc/src/sgml/nls-programmer.html
? doc/src/sgml/nls-translator.html
? doc/src/sgml/nls.html
? doc/src/sgml/notation.html
? doc/src/sgml/oid2name.html
? doc/src/sgml/overview.html
? doc/src/sgml/pageinspect.html
? doc/src/sgml/parser-stage.html
? doc/src/sgml/performance-tips.html
? doc/src/sgml/perm-functions.html
? doc/src/sgml/pgbench.html
? doc/src/sgml/pgbuffercache.html
? doc/src/sgml/pgcrypto.html
? doc/src/sgml/pgfreespacemap.html
? doc/src/sgml/pgrowlocks.html
? doc/src/sgml/pgstandby.html
? doc/src/sgml/pgstatstatements.html
? doc/src/sgml/pgstattuple.html
? doc/src/sgml/pgtrgm.html
? doc/src/sgml/planner-optimizer.html
? doc/src/sgml/planner-stats-details.html
? doc/src/sgml/planner-stats.html
? doc/src/sgml/plhandler.html
? doc/src/sgml/plperl-data.html
? doc/src/sgml/plperl-database.html
? doc/src/sgml/plperl-funcs.html
? doc/src/sgml/plperl-global.html
? doc/src/sgml/plperl-missing.html
? doc/src/sgml/plperl-triggers.html
? doc/src/sgml/plperl-trusted.html
? doc/src/sgml/plperl.html
? doc/src/sgml/plpgsql-control-structures.html
? doc/src/sgml/plpgsql-cursors.html
? doc/src/sgml/plpgsql-declarations.html
? doc/src/sgml/plpgsql-development-tips.html
? doc/src/sgml/plpgsql-errors-and-messages.html
? doc/src/sgml/plpgsql-expressions.html
? doc/src/sgml/plpgsql-implementation.html
? doc/src/sgml/plpgsql-overview.html
? doc/src/sgml/plpgsql-porting.html
? doc/src/sgml/plpgsql-statements.html
? doc/src/sgml/plpgsql-structure.html
? doc/src/sgml/plpgsql-trigger.html
? doc/src/sgml/plpgsql.html
? doc/src/sgml/plpython-database.html
? doc/src/sgml/plpython-funcs.html
? doc/src/sgml/plpython-trigger.html
? doc/src/sgml/plpython.html
? doc/src/sgml/pltcl-data.html
? doc/src/sgml/pltcl-dbaccess.html
? doc/src/sgml/pltcl-functions.html
? doc/src/sgml/pltcl-global.html
? doc/src/sgml/pltcl-overview.html
? doc/src/sgml/pltcl-procnames.html
? doc/src/sgml/pltcl-trigger.html
? doc/src/sgml/pltcl-unknown.html
? doc/src/sgml/pltcl.html
? doc/src/sgml/populate.html
? doc/src/sgml/postgres-user.html
? doc/src/sgml/preface.html
? doc/src/sgml/preventing-server-spoofing.html
? doc/src/sgml/privileges.html
? doc/src/sgml/protocol-changes.html
? doc/src/sgml/protocol-error-fields.html
? doc/src/sgml/protocol-flow.html
? doc/src/sgml/protocol-message-formats.html
? doc/src/sgml/protocol-message-types.html
? doc/src/sgml/protocol-overview.html
? doc/src/sgml/protocol.html
? doc/src/sgml/queries-limit.html
? doc/src/sgml/queries-order.html
? doc/src/sgml/queries-overview.html
? doc/src/sgml/queries-select-lists.html
? doc/src/sgml/queries-table-expressions.html
? doc/src/sgml/queries-union.html
? doc/src/sgml/queries-values.html
? doc/src/sgml/queries-with.html
? doc/src/sgml/queries.html
? doc/src/sgml/query-path.html
? doc/src/sgml/querytree.html
? doc/src/sgml/reference-client.html
? doc/src/sgml/reference-server.html
? doc/src/sgml/reference.html
? doc/src/sgml/regress-coverage.html
? doc/src/sgml/regress-evaluation.html
? doc/src/sgml/regress-run.html
? doc/src/sgml/regress-variant.html
? doc/src/sgml/regress.html
? doc/src/sgml/release-0-01.html
? doc/src/sgml/release-0-02.html
? doc/src/sgml/release-0-03.html
? doc/src/sgml/release-1-0.html
? doc/src/sgml/release-1-01.html
? doc/src/sgml/release-1-02.html
? doc/src/sgml/release-1-09.html
? doc/src/sgml/release-6-0.html
? doc/src/sgml/release-6-1-1.html
? doc/src/sgml/release-6-1.html
? doc/src/sgml/release-6-2-1.html
? doc/src/sgml/release-6-2.html
? doc/src/sgml/release-6-3-1.html
? doc/src/sgml/release-6-3-2.html
? doc/src/sgml/release-6-3.html
? doc/src/sgml/release-6-4-1.html
? doc/src/sgml/release-6-4-2.html
? doc/src/sgml/release-6-4.html
? doc/src/sgml/release-6-5-1.html
? doc/src/sgml/release-6-5-2.html
? doc/src/sgml/release-6-5-3.html
? doc/src/sgml/release-6-5.html
? doc/src/sgml/release-7-0-1.html
? doc/src/sgml/release-7-0-2.html
? doc/src/sgml/release-7-0-3.html
? doc/src/sgml/release-7-0.html
? doc/src/sgml/release-7-1-1.html
? doc/src/sgml/release-7-1-2.html
? doc/src/sgml/release-7-1-3.html
? doc/src/sgml/release-7-1.html
? doc/src/sgml/release-7-2-1.html
? doc/src/sgml/release-7-2-2.html
? doc/src/sgml/release-7-2-3.html
? doc/src/sgml/release-7-2-4.html
? doc/src/sgml/release-7-2-5.html
? doc/src/sgml/release-7-2-6.html
? doc/src/sgml/release-7-2-7.html
? doc/src/sgml/release-7-2-8.html
? doc/src/sgml/release-7-2.html
? doc/src/sgml/release-7-3-1.html
? doc/src/sgml/release-7-3-10.html
? doc/src/sgml/release-7-3-11.html
? doc/src/sgml/release-7-3-12.html
? doc/src/sgml/release-7-3-13.html
? doc/src/sgml/release-7-3-14.html
? doc/src/sgml/release-7-3-15.html
? doc/src/sgml/release-7-3-16.html
? doc/src/sgml/release-7-3-17.html
? doc/src/sgml/release-7-3-18.html
? doc/src/sgml/release-7-3-19.html
? doc/src/sgml/release-7-3-2.html
? doc/src/sgml/release-7-3-20.html
? doc/src/sgml/release-7-3-21.html
? doc/src/sgml/release-7-3-3.html
? doc/src/sgml/release-7-3-4.html
? doc/src/sgml/release-7-3-5.html
? doc/src/sgml/release-7-3-6.html
? doc/src/sgml/release-7-3-7.html
? doc/src/sgml/release-7-3-8.html
? doc/src/sgml/release-7-3-9.html
? doc/src/sgml/release-7-3.html
? doc/src/sgml/release-7-4-1.html
? doc/src/sgml/release-7-4-10.html
? doc/src/sgml/release-7-4-11.html
? doc/src/sgml/release-7-4-12.html
? doc/src/sgml/release-7-4-13.html
? doc/src/sgml/release-7-4-14.html
? doc/src/sgml/release-7-4-15.html
? doc/src/sgml/release-7-4-16.html
? doc/src/sgml/release-7-4-17.html
? doc/src/sgml/release-7-4-18.html
? doc/src/sgml/release-7-4-19.html
? doc/src/sgml/release-7-4-2.html
? doc/src/sgml/release-7-4-20.html
? doc/src/sgml/release-7-4-21.html
? doc/src/sgml/release-7-4-22.html
? doc/src/sgml/release-7-4-23.html
? doc/src/sgml/release-7-4-24.html
? doc/src/sgml/release-7-4-25.html
? doc/src/sgml/release-7-4-3.html
? doc/src/sgml/release-7-4-4.html
? doc/src/sgml/release-7-4-5.html
? doc/src/sgml/release-7-4-6.html
? doc/src/sgml/release-7-4-7.html
? doc/src/sgml/release-7-4-8.html
? doc/src/sgml/release-7-4-9.html
? doc/src/sgml/release-7-4.html
? doc/src/sgml/release-8-0-1.html
? doc/src/sgml/release-8-0-10.html
? doc/src/sgml/release-8-0-11.html
? doc/src/sgml/release-8-0-12.html
? doc/src/sgml/release-8-0-13.html
? doc/src/sgml/release-8-0-14.html
? doc/src/sgml/release-8-0-15.html
? doc/src/sgml/release-8-0-16.html
? doc/src/sgml/release-8-0-17.html
? doc/src/sgml/release-8-0-18.html
? doc/src/sgml/release-8-0-19.html
? doc/src/sgml/release-8-0-2.html
? doc/src/sgml/release-8-0-20.html
? doc/src/sgml/release-8-0-21.html
? doc/src/sgml/release-8-0-3.html
? doc/src/sgml/release-8-0-4.html
? doc/src/sgml/release-8-0-5.html
? doc/src/sgml/release-8-0-6.html
? doc/src/sgml/release-8-0-7.html
? doc/src/sgml/release-8-0-8.html
? doc/src/sgml/release-8-0-9.html
? doc/src/sgml/release-8-0.html
? doc/src/sgml/release-8-1-1.html
? doc/src/sgml/release-8-1-10.html
? doc/src/sgml/release-8-1-11.html
? doc/src/sgml/release-8-1-12.html
? doc/src/sgml/release-8-1-13.html
? doc/src/sgml/release-8-1-14.html
? doc/src/sgml/release-8-1-15.html
? doc/src/sgml/release-8-1-16.html
? doc/src/sgml/release-8-1-17.html
? doc/src/sgml/release-8-1-2.html
? doc/src/sgml/release-8-1-3.html
? doc/src/sgml/release-8-1-4.html
? doc/src/sgml/release-8-1-5.html
? doc/src/sgml/release-8-1-6.html
? doc/src/sgml/release-8-1-7.html
? doc/src/sgml/release-8-1-8.html
? doc/src/sgml/release-8-1-9.html
? doc/src/sgml/release-8-1.html
? doc/src/sgml/release-8-2-1.html
? doc/src/sgml/release-8-2-10.html
? doc/src/sgml/release-8-2-11.html
? doc/src/sgml/release-8-2-12.html
? doc/src/sgml/release-8-2-13.html
? doc/src/sgml/release-8-2-2.html
? doc/src/sgml/release-8-2-3.html
? doc/src/sgml/release-8-2-4.html
? doc/src/sgml/release-8-2-5.html
? doc/src/sgml/release-8-2-6.html
? doc/src/sgml/release-8-2-7.html
? doc/src/sgml/release-8-2-8.html
? doc/src/sgml/release-8-2-9.html
? doc/src/sgml/release-8-2.html
? doc/src/sgml/release-8-3-1.html
? doc/src/sgml/release-8-3-2.html
? doc/src/sgml/release-8-3-3.html
? doc/src/sgml/release-8-3-4.html
? doc/src/sgml/release-8-3-5.html
? doc/src/sgml/release-8-3-6.html
? doc/src/sgml/release-8-3-7.html
? doc/src/sgml/release-8-3.html
? doc/src/sgml/release-8-4.html
? doc/src/sgml/release.html
? doc/src/sgml/resources.html
? doc/src/sgml/role-attributes.html
? doc/src/sgml/role-membership.html
? doc/src/sgml/routine-reindex.html
? doc/src/sgml/routine-vacuuming.html
? doc/src/sgml/row-estimation-examples.html
? doc/src/sgml/rowtypes.html
? doc/src/sgml/rsync.html
? doc/src/sgml/rule-system.html
? doc/src/sgml/rules-privileges.html
? doc/src/sgml/rules-status.html
? doc/src/sgml/rules-triggers.html
? doc/src/sgml/rules-update.html
? doc/src/sgml/rules-views.html
? doc/src/sgml/rules.html
? doc/src/sgml/runtime-config-autovacuum.html
? doc/src/sgml/runtime-config-client.html
? doc/src/sgml/runtime-config-compatible.html
? doc/src/sgml/runtime-config-connection.html
? doc/src/sgml/runtime-config-custom.html
? doc/src/sgml/runtime-config-developer.html
? doc/src/sgml/runtime-config-file-locations.html
? doc/src/sgml/runtime-config-locks.html
? doc/src/sgml/runtime-config-logging.html
? doc/src/sgml/runtime-config-preset.html
? doc/src/sgml/runtime-config-query.html
? doc/src/sgml/runtime-config-resource.html
? doc/src/sgml/runtime-config-short.html
? doc/src/sgml/runtime-config-statistics.html
? doc/src/sgml/runtime-config-wal.html
? doc/src/sgml/runtime-config.html
? doc/src/sgml/runtime.html
? doc/src/sgml/seg.html
? doc/src/sgml/server-programming.html
? doc/src/sgml/server-shutdown.html
? doc/src/sgml/server-start.html
? doc/src/sgml/source-format.html
? doc/src/sgml/source.html
? doc/src/sgml/spi-examples.html
? doc/src/sgml/spi-interface-support.html
? doc/src/sgml/spi-interface.html
? doc/src/sgml/spi-memory.html
? doc/src/sgml/spi-realloc.html
? doc/src/sgml/spi-spi-connect.html
? doc/src/sgml/spi-spi-copytuple.html
? doc/src/sgml/spi-spi-cursor-close.html
? doc/src/sgml/spi-spi-cursor-fetch.html
? doc/src/sgml/spi-spi-cursor-find.html
? doc/src/sgml/spi-spi-cursor-move.html
? doc/src/sgml/spi-spi-cursor-open-with-args.html
? doc/src/sgml/spi-spi-cursor-open.html
? doc/src/sgml/spi-spi-exec.html
? doc/src/sgml/spi-spi-execp.html
? doc/src/sgml/spi-spi-execute-plan.html
? doc/src/sgml/spi-spi-execute-with-args.html
? doc/src/sgml/spi-spi-execute.html
? doc/src/sgml/spi-spi-finish.html
? doc/src/sgml/spi-spi-fname.html
? doc/src/sgml/spi-spi-fnumber.html
? doc/src/sgml/spi-spi-freeplan.html
? doc/src/sgml/spi-spi-freetuple.html
? doc/src/sgml/spi-spi-freetupletable.html
? doc/src/sgml/spi-spi-getargcount.html
? doc/src/sgml/spi-spi-getargtypeid.html
? doc/src/sgml/spi-spi-getbinval.html
? doc/src/sgml/spi-spi-getnspname.html
? doc/src/sgml/spi-spi-getrelname.html
? doc/src/sgml/spi-spi-gettype.html
? doc/src/sgml/spi-spi-gettypeid.html
? doc/src/sgml/spi-spi-getvalue.html
? doc/src/sgml/spi-spi-is-cursor-plan.html
? doc/src/sgml/spi-spi-modifytuple.html
? doc/src/sgml/spi-spi-palloc.html
? doc/src/sgml/spi-spi-pfree.html
? doc/src/sgml/spi-spi-pop.html
? doc/src/sgml/spi-spi-prepare-cursor.html
? doc/src/sgml/spi-spi-prepare.html
? doc/src/sgml/spi-spi-push.html
? doc/src/sgml/spi-spi-returntuple.html
? doc/src/sgml/spi-spi-saveplan.html
? doc/src/sgml/spi-spi-scroll-cursor-fetch.html
? doc/src/sgml/spi-spi-scroll-cursor-move.html
? doc/src/sgml/spi-visibility.html
? doc/src/sgml/spi.html
? doc/src/sgml/sql-abort.html
? doc/src/sgml/sql-alteraggregate.html
? doc/src/sgml/sql-alterconversion.html
? doc/src/sgml/sql-alterdatabase.html
? doc/src/sgml/sql-alterdomain.html
? doc/src/sgml/sql-alterforeigndatawrapper.html
? doc/src/sgml/sql-alterfunction.html
? doc/src/sgml/sql-altergroup.html
? doc/src/sgml/sql-alterindex.html
? doc/src/sgml/sql-alterlanguage.html
? doc/src/sgml/sql-alteropclass.html
? doc/src/sgml/sql-alteroperator.html
? doc/src/sgml/sql-alteropfamily.html
? doc/src/sgml/sql-alterrole.html
? doc/src/sgml/sql-alterschema.html
? doc/src/sgml/sql-altersequence.html
? doc/src/sgml/sql-alterserver.html
? doc/src/sgml/sql-altertable.html
? doc/src/sgml/sql-altertablespace.html
? doc/src/sgml/sql-altertrigger.html
? doc/src/sgml/sql-altertsconfig.html
? doc/src/sgml/sql-altertsdictionary.html
? doc/src/sgml/sql-altertsparser.html
? doc/src/sgml/sql-altertstemplate.html
? doc/src/sgml/sql-altertype.html
? doc/src/sgml/sql-alteruser.html
? doc/src/sgml/sql-alterusermapping.html
? doc/src/sgml/sql-alterview.html
? doc/src/sgml/sql-analyze.html
? doc/src/sgml/sql-begin.html
? doc/src/sgml/sql-checkpoint.html
? doc/src/sgml/sql-close.html
? doc/src/sgml/sql-cluster.html
? doc/src/sgml/sql-commands.html
? doc/src/sgml/sql-comment.html
? doc/src/sgml/sql-commit-prepared.html
? doc/src/sgml/sql-commit.html
? doc/src/sgml/sql-copy.html
? doc/src/sgml/sql-createaggregate.html
? doc/src/sgml/sql-createcast.html
? doc/src/sgml/sql-createconstraint.html
? doc/src/sgml/sql-createconversion.html
? doc/src/sgml/sql-createdatabase.html
? doc/src/sgml/sql-createdomain.html
? doc/src/sgml/sql-createforeigndatawrapper.html
? doc/src/sgml/sql-createfunction.html
? doc/src/sgml/sql-creategroup.html
? doc/src/sgml/sql-createindex.html
? doc/src/sgml/sql-createlanguage.html
? doc/src/sgml/sql-createopclass.html
? doc/src/sgml/sql-createoperator.html
? doc/src/sgml/sql-createopfamily.html
? doc/src/sgml/sql-createrole.html
? doc/src/sgml/sql-createrule.html
? doc/src/sgml/sql-createschema.html
? doc/src/sgml/sql-createsequence.html
? doc/src/sgml/sql-createserver.html
? doc/src/sgml/sql-createtable.html
? doc/src/sgml/sql-createtableas.html
? doc/src/sgml/sql-createtablespace.html
? doc/src/sgml/sql-createtrigger.html
? doc/src/sgml/sql-createtsconfig.html
? doc/src/sgml/sql-createtsdictionary.html
? doc/src/sgml/sql-createtsparser.html
? doc/src/sgml/sql-createtstemplate.html
? doc/src/sgml/sql-createtype.html
? doc/src/sgml/sql-createuser.html
? doc/src/sgml/sql-createusermapping.html
? doc/src/sgml/sql-createview.html
? doc/src/sgml/sql-deallocate.html
? doc/src/sgml/sql-declare.html
? doc/src/sgml/sql-delete.html
? doc/src/sgml/sql-discard.html
? doc/src/sgml/sql-drop-owned.html
? doc/src/sgml/sql-dropaggregate.html
? doc/src/sgml/sql-dropcast.html
? doc/src/sgml/sql-dropconversion.html
? doc/src/sgml/sql-dropdatabase.html
? doc/src/sgml/sql-dropdomain.html
? doc/src/sgml/sql-dropforeigndatawrapper.html
? doc/src/sgml/sql-dropfunction.html
? doc/src/sgml/sql-dropgroup.html
? doc/src/sgml/sql-dropindex.html
? doc/src/sgml/sql-droplanguage.html
? doc/src/sgml/sql-dropopclass.html
? doc/src/sgml/sql-dropoperator.html
? doc/src/sgml/sql-dropopfamily.html
? doc/src/sgml/sql-droprole.html
? doc/src/sgml/sql-droprule.html
? doc/src/sgml/sql-dropschema.html
? doc/src/sgml/sql-dropsequence.html
? doc/src/sgml/sql-dropserver.html
? doc/src/sgml/sql-droptable.html
? doc/src/sgml/sql-droptablespace.html
? doc/src/sgml/sql-droptrigger.html
? doc/src/sgml/sql-droptsconfig.html
? doc/src/sgml/sql-droptsdictionary.html
? doc/src/sgml/sql-droptsparser.html
? doc/src/sgml/sql-droptstemplate.html
? doc/src/sgml/sql-droptype.html
? doc/src/sgml/sql-dropuser.html
? doc/src/sgml/sql-dropusermapping.html
? doc/src/sgml/sql-dropview.html
? doc/src/sgml/sql-end.html
? doc/src/sgml/sql-execute.html
? doc/src/sgml/sql-explain.html
? doc/src/sgml/sql-expressions.html
? doc/src/sgml/sql-fetch.html
? doc/src/sgml/sql-grant.html
? doc/src/sgml/sql-insert.html
? doc/src/sgml/sql-keywords-appendix.html
? doc/src/sgml/sql-listen.html
? doc/src/sgml/sql-load.html
? doc/src/sgml/sql-lock.html
? doc/src/sgml/sql-move.html
? doc/src/sgml/sql-notify.html
? doc/src/sgml/sql-prepare-transaction.html
? doc/src/sgml/sql-prepare.html
? doc/src/sgml/sql-reassign-owned.html
? doc/src/sgml/sql-reindex.html
? doc/src/sgml/sql-release-savepoint.html
? doc/src/sgml/sql-reset.html
? doc/src/sgml/sql-revoke.html
? doc/src/sgml/sql-rollback-prepared.html
? doc/src/sgml/sql-rollback-to.html
? doc/src/sgml/sql-rollback.html
? doc/src/sgml/sql-savepoint.html
? doc/src/sgml/sql-select.html
? doc/src/sgml/sql-selectinto.html
? doc/src/sgml/sql-set-constraints.html
? doc/src/sgml/sql-set-role.html
? doc/src/sgml/sql-set-session-authorization.html
? doc/src/sgml/sql-set-transaction.html
? doc/src/sgml/sql-set.html
? doc/src/sgml/sql-show.html
? doc/src/sgml/sql-start-transaction.html
? doc/src/sgml/sql-syntax-lexical.html
? doc/src/sgml/sql-syntax.html
? doc/src/sgml/sql-truncate.html
? doc/src/sgml/sql-unlisten.html
? doc/src/sgml/sql-update.html
? doc/src/sgml/sql-vacuum.html
? doc/src/sgml/sql-values.html
? doc/src/sgml/sql.html
? doc/src/sgml/ssh-tunnels.html
? doc/src/sgml/ssl-tcp.html
? doc/src/sgml/sslinfo.html
? doc/src/sgml/storage-file-layout.html
? doc/src/sgml/storage-fsm.html
? doc/src/sgml/storage-page-layout.html
? doc/src/sgml/storage-toast.html
? doc/src/sgml/storage.html
? doc/src/sgml/supported-platforms.html
? doc/src/sgml/tablefunc.html
? doc/src/sgml/test-parser.html
? doc/src/sgml/textsearch-configuration.html
? doc/src/sgml/textsearch-controls.html
? doc/src/sgml/textsearch-debugging.html
? doc/src/sgml/textsearch-dictionaries.html
? doc/src/sgml/textsearch-features.html
? doc/src/sgml/textsearch-indexes.html
? doc/src/sgml/textsearch-intro.html
? doc/src/sgml/textsearch-limitations.html
? doc/src/sgml/textsearch-migration.html
? doc/src/sgml/textsearch-parsers.html
? doc/src/sgml/textsearch-psql.html
? doc/src/sgml/textsearch-tables.html
? doc/src/sgml/textsearch.html
? doc/src/sgml/transaction-iso.html
? doc/src/sgml/trigger-datachanges.html
? doc/src/sgml/trigger-definition.html
? doc/src/sgml/trigger-example.html
? doc/src/sgml/trigger-interface.html
? doc/src/sgml/triggers.html
? doc/src/sgml/tsearch2.html
? doc/src/sgml/tutorial-accessdb.html
? doc/src/sgml/tutorial-advanced-intro.html
? doc/src/sgml/tutorial-advanced.html
? doc/src/sgml/tutorial-agg.html
? doc/src/sgml/tutorial-arch.html
? doc/src/sgml/tutorial-concepts.html
? doc/src/sgml/tutorial-conclusion.html
? doc/src/sgml/tutorial-createdb.html
? doc/src/sgml/tutorial-delete.html
? doc/src/sgml/tutorial-fk.html
? doc/src/sgml/tutorial-inheritance.html
? doc/src/sgml/tutorial-install.html
? doc/src/sgml/tutorial-join.html
? doc/src/sgml/tutorial-populate.html
? doc/src/sgml/tutorial-select.html
? doc/src/sgml/tutorial-sql-intro.html
? doc/src/sgml/tutorial-sql.html
? doc/src/sgml/tutorial-start.html
? doc/src/sgml/tutorial-table.html
? doc/src/sgml/tutorial-transactions.html
? doc/src/sgml/tutorial-update.html
? doc/src/sgml/tutorial-views.html
? doc/src/sgml/tutorial-window.html
? doc/src/sgml/tutorial.html
? doc/src/sgml/typeconv-func.html
? doc/src/sgml/typeconv-oper.html
? doc/src/sgml/typeconv-overview.html
? doc/src/sgml/typeconv-query.html
? doc/src/sgml/typeconv-union-case.html
? doc/src/sgml/typeconv.html
? doc/src/sgml/unsupported-features-sql-standard.html
? doc/src/sgml/user-manag.html
? doc/src/sgml/using-explain.html
? doc/src/sgml/uuid-ossp.html
? doc/src/sgml/vacuumlo.html
? doc/src/sgml/version.sgml
? doc/src/sgml/view-pg-cursors.html
? doc/src/sgml/view-pg-group.html
? doc/src/sgml/view-pg-indexes.html
? doc/src/sgml/view-pg-locks.html
? doc/src/sgml/view-pg-prepared-statements.html
? doc/src/sgml/view-pg-prepared-xacts.html
? doc/src/sgml/view-pg-roles.html
? doc/src/sgml/view-pg-rules.html
? doc/src/sgml/view-pg-settings.html
? doc/src/sgml/view-pg-shadow.html
? doc/src/sgml/view-pg-stats.html
? doc/src/sgml/view-pg-tables.html
? doc/src/sgml/view-pg-timezone-abbrevs.html
? doc/src/sgml/view-pg-timezone-names.html
? doc/src/sgml/view-pg-user-mappings.html
? doc/src/sgml/view-pg-user.html
? doc/src/sgml/view-pg-views.html
? doc/src/sgml/views-overview.html
? doc/src/sgml/wal-async-commit.html
? doc/src/sgml/wal-configuration.html
? doc/src/sgml/wal-internals.html
? doc/src/sgml/wal-intro.html
? doc/src/sgml/wal-reliability.html
? doc/src/sgml/wal.html
? doc/src/sgml/warm-standby.html
? doc/src/sgml/xaggr.html
? doc/src/sgml/xfunc-c.html
? doc/src/sgml/xfunc-internal.html
? doc/src/sgml/xfunc-overload.html
? doc/src/sgml/xfunc-pl.html
? doc/src/sgml/xfunc-sql.html
? doc/src/sgml/xfunc-volatility.html
? doc/src/sgml/xfunc.html
? doc/src/sgml/xindex.html
? doc/src/sgml/xml2.html
? doc/src/sgml/xoper-optimization.html
? doc/src/sgml/xoper.html
? doc/src/sgml/xplang-install.html
? doc/src/sgml/xplang.html
? doc/src/sgml/xtypes.html
? src/Makefile.global
? src/backend/postgres
? src/backend/catalog/postgres.bki
? src/backend/catalog/postgres.description
? src/backend/catalog/postgres.shdescription
? src/backend/snowball/snowball_create.sql
? src/backend/utils/probes.h
? src/backend/utils/mb/conversion_procs/conversion_create.sql
? src/bin/initdb/initdb
? src/bin/pg_config/pg_config
? src/bin/pg_controldata/pg_controldata
? src/bin/pg_ctl/pg_ctl
? src/bin/pg_dump/pg_dump
? src/bin/pg_dump/pg_dumpall
? src/bin/pg_dump/pg_restore
? src/bin/pg_resetxlog/pg_resetxlog
? src/bin/psql/psql
? src/bin/scripts/clusterdb
? src/bin/scripts/createdb
? src/bin/scripts/createlang
? src/bin/scripts/createuser
? src/bin/scripts/dropdb
? src/bin/scripts/droplang
? src/bin/scripts/dropuser
? src/bin/scripts/reindexdb
? src/bin/scripts/vacuumdb
? src/include/pg_config.h
? src/include/stamp-h
? src/interfaces/ecpg/compatlib/exports.list
? src/interfaces/ecpg/compatlib/libecpg_compat.so.3.1
? src/interfaces/ecpg/ecpglib/exports.list
? src/interfaces/ecpg/ecpglib/libecpg.so.6.1
? src/interfaces/ecpg/include/ecpg_config.h
? src/interfaces/ecpg/include/stamp-h
? src/interfaces/ecpg/pgtypeslib/exports.list
? src/interfaces/ecpg/pgtypeslib/libpgtypes.so.3.1
? src/interfaces/ecpg/preproc/ecpg
? src/interfaces/libpq/exports.list
? src/interfaces/libpq/libpq.so.5.2
? src/port/pg_config_paths.h
? src/test/regress/pg_regress
? src/test/regress/testtablespace
? src/timezone/zic
Index: contrib/pg_standby/pg_standby.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pg_standby/pg_standby.c,v
retrieving revision 1.21
diff -c -r1.21 pg_standby.c
*** contrib/pg_standby/pg_standby.c 26 Mar 2009 22:29:13 -0000 1.21
--- contrib/pg_standby/pg_standby.c 3 Apr 2009 03:20:41 -0000
***************
*** 52,58 ****
int keepfiles = 0; /* number of WAL files to keep, 0 keep all */
int maxretries = 3; /* number of retries on restore command */
bool debug = false; /* are we debugging? */
- bool triggered = false; /* have we been triggered? */
bool need_cleanup = false; /* do we need to remove files from
* archive? */
--- 52,57 ----
***************
*** 69,74 ****
--- 68,99 ----
char exclusiveCleanupFileName[MAXPGPATH]; /* the file we need to
* get from archive */
+ /*
+ * Two types of failover are supported (smart and fast failover).
+ *
+ * The content of the trigger file determines the type of failover.
+ * If the trigger file labeled "smart" exists, smart failover is chosen;
+ * pg_standby acts as cp or ln command itself, and recovery waits to
+ * finish until all the available WAL files are applied. It's
+ * guaranteed that no "available" commit transactions are lost. But
+ * it might take some times before finishing recovery.
+ *
+ * On the other hand, the existence of the trigger file with "fast"
+ * causes recovery to end immediately even if the available WAL files
+ * remain. So, some transactions might be lost.
+ *
+ * An empty trigger file performs smart failover.
+ *
+ * Fast failover is triggered by the signal (SIGUSR1 or SIGINT).
+ *
+ * A timeout causes smart failover.
+ */
+ #define NoFailover 0
+ #define SmartFailover 1
+ #define FastFailover 2
+
+ static int Failover = NoFailover;
+
#define RESTORE_COMMAND_COPY 0
#define RESTORE_COMMAND_LINK 1
int restoreCommandType;
***************
*** 108,114 ****
*
* As an example, and probably the common case, we use either
* cp/ln commands on *nix, or copy/move command on Windows.
- *
*/
static void
CustomizableInitialize(void)
--- 133,138 ----
***************
*** 357,363 ****
static bool
CheckForExternalTrigger(void)
{
! int rc;
/*
* Look for a trigger file, if that option has been selected
--- 381,388 ----
static bool
CheckForExternalTrigger(void)
{
! char buf[32];
! FILE *fd;
/*
* Look for a trigger file, if that option has been selected
***************
*** 365,374 ****
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (triggerPath && stat(triggerPath, &stat_buf) == 0)
{
! fprintf(stderr, "trigger file found\n");
fflush(stderr);
/*
* If trigger file found, we *must* delete it. Here's why: When
--- 390,439 ----
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (!triggerPath || stat(triggerPath, &stat_buf) != 0)
! return false;
!
! /*
! * An empty trigger file performs smart failover
! */
! if (stat_buf.st_size == 0)
{
! Failover = SmartFailover;
! fprintf(stderr, "trigger file found: smart failover\n");
fflush(stderr);
+ return true;
+ }
+
+ if ((fd = fopen(triggerPath, "r")) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not open \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ return false;
+ }
+
+ if (fgets(buf, sizeof(buf), fd) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not read \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ fclose(fd);
+ return false;
+ }
+
+ fclose(fd);
+
+ if (strspn(buf, "smart") == 5 && strncmp(buf, "smart", 5) == 0)
+ {
+ Failover = SmartFailover;
+ fprintf(stderr, "trigger file found: smart failover\n");
+ fflush(stderr);
+ return true;
+ }
+
+ if (strspn(buf, "fast") == 4 && strncmp(buf, "fast", 4) == 0)
+ {
+ int rc;
/*
* If trigger file found, we *must* delete it. Here's why: When
***************
*** 379,391 ****
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
return true;
}
!
return false;
}
--- 444,464 ----
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s",
! triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
+
+ Failover = FastFailover;
+ fprintf(stderr, "trigger file found: fast failover\n");
+ fflush(stderr);
return true;
}
!
! fprintf(stderr, "WARNING: invalid content in \"%s\"\n",
! triggerPath);
! fflush(stderr);
return false;
}
***************
*** 552,559 ****
break;
case 't': /* Trigger file */
triggerPath = optarg;
- if (CheckForExternalTrigger())
- exit(1); /* Normal exit, with non-zero */
break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
--- 625,630 ----
***************
*** 676,697 ****
/*
* Main wait loop
*/
! while (!CustomizableNextWALFileReady() && !triggered)
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! triggered = true;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit\n");
fflush(stderr);
}
}
else
{
if (debug)
{
--- 747,781 ----
/*
* Main wait loop
*/
! while (!CheckForExternalTrigger() && !CustomizableNextWALFileReady())
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! Failover = FastFailover;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit: fast failover\n");
fflush(stderr);
}
+ break;
}
else
{
+ waittime += sleeptime;
+ if (waittime >= maxwaittime && maxwaittime > 0)
+ {
+ Failover = FastFailover;
+ if (debug)
+ {
+ fprintf(stderr, "\nTimed out after %d seconds: fast failover\n",
+ waittime);
+ fflush(stderr);
+ }
+ break;
+ }
if (debug)
{
***************
*** 700,722 ****
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
-
- waittime += sleeptime;
-
- if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0)))
- {
- triggered = true;
- if (debug && waittime >= maxwaittime && maxwaittime > 0)
- fprintf(stderr, "\nTimed out after %d seconds\n", waittime);
- }
}
}
/*
* Action on exit
*/
! if (triggered)
! exit(1); /* Normal exit, with non-zero */
/*
* Once we have restored this file successfully we can remove some prior
--- 784,797 ----
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
}
}
/*
* Action on exit
*/
! if (Failover == FastFailover)
! exit(1);
/*
* Once we have restored this file successfully we can remove some prior
Index: doc/src/sgml/pgstandby.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/pgstandby.sgml,v
retrieving revision 2.7
diff -c -r2.7 pgstandby.sgml
*** doc/src/sgml/pgstandby.sgml 27 Feb 2009 09:30:21 -0000 2.7
--- doc/src/sgml/pgstandby.sgml 3 Apr 2009 03:20:41 -0000
***************
*** 92,97 ****
--- 92,138 ----
is specified,
the <replaceable>archivelocation</> directory must be writable too.
</para>
+ <para>
+ There are two ways to fail over a <quote>warm standby</> database server.
+ You control the type of failover by creating different trigger files
+ (if <literal>-t</> has been specified).
+
+ <variablelist>
+ <varlistentry>
+ <term>Smart Failover</term>
+ <listitem>
+ <para>
+ If the trigger file labeled <literal>smart</> exists,
+ <application>pg_standby</application> acts as
+ <literal>cp</> or <literal>ln</> command itself,
+ and recovery waits to finish until all the available
+ WAL files are applied. It's guaranteed that no
+ <emphasis>available</> transactions are lost.
+ But it might take some times before finishing failover.
+ </para>
+ <para>
+ An empty trigger file also smartly performs failover.
+ </para>
+ <para>
+ Note that the trigger file remains even after failover.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Fast Failover</term>
+ <listitem>
+ <para>
+ The existence of the trigger file labeled <literal>fast</>
+ causes recovery to end immediately even if the available
+ WAL files remain. So, some transactions might be lost.
+ </para>
+ <para>
+ Note that the trigger file is removed at failover.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
<table>
<title><application>pg_standby</> options</title>
***************
*** 177,184 ****
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should cause recovery to end
! whether or not the next WAL file is available.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
--- 218,224 ----
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should perform failover.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
***************
*** 190,196 ****
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up.
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
--- 230,237 ----
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up
! (Fast Failover).
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
***************
*** 236,242 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 277,284 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
***************
*** 277,283 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 319,326 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
On Fri, Apr 3, 2009 at 5:42 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Here is the patch;
- Smart failover is chosen if the trigger file labeled "smart" or
an empty one exists.
- Fast failover is chosen if the trigger file labeled "fast" exists,
the signal (SIGUSR1 or SIGINT) is received or the wait timeout
happens.
After some further thoughts, +1 for this approach too.
I think you imply 'containing "smart"' not 'labeled "smart"'.
"Labeled" is confusing IMHO.
+1 to change the default behaviour too. All the people discovering the
current behaviour are totally surprised.
--
Guillaume
Hi,
On Wed, Apr 8, 2009 at 6:56 AM, Guillaume Smet <guillaume.smet@gmail.com> wrote:
On Fri, Apr 3, 2009 at 5:42 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Here is the patch;
- Smart failover is chosen if the trigger file labeled "smart" or
an empty one exists.
- Fast failover is chosen if the trigger file labeled "fast" exists,
the signal (SIGUSR1 or SIGINT) is received or the wait timeout
happens.After some further thoughts, +1 for this approach too.
I think you imply 'containing "smart"' not 'labeled "smart"'.
"Labeled" is confusing IMHO.
Thanks for the comment!
I corrected such confusing expression.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
pgstandby_new_trigger_0408.patchapplication/octet-stream; name=pgstandby_new_trigger_0408.patchDownload
? GNUmakefile
? config.log
? config.status
? contrib/pg_standby/pg_standby
? contrib/pgbench/pgbench
? doc/src/sgml/HTML.index
? doc/src/sgml/HTML.index.start
? doc/src/sgml/LEGALNOTICE.html
? doc/src/sgml/acronyms.html
? doc/src/sgml/admin.html
? doc/src/sgml/adminpack.html
? doc/src/sgml/anoncvs.html
? doc/src/sgml/app-clusterdb.html
? doc/src/sgml/app-createdb.html
? doc/src/sgml/app-createlang.html
? doc/src/sgml/app-createuser.html
? doc/src/sgml/app-dropdb.html
? doc/src/sgml/app-droplang.html
? doc/src/sgml/app-dropuser.html
? doc/src/sgml/app-ecpg.html
? doc/src/sgml/app-initdb.html
? doc/src/sgml/app-pg-ctl.html
? doc/src/sgml/app-pg-dumpall.html
? doc/src/sgml/app-pgconfig.html
? doc/src/sgml/app-pgcontroldata.html
? doc/src/sgml/app-pgdump.html
? doc/src/sgml/app-pgresetxlog.html
? doc/src/sgml/app-pgrestore.html
? doc/src/sgml/app-postgres.html
? doc/src/sgml/app-postmaster.html
? doc/src/sgml/app-psql.html
? doc/src/sgml/app-reindexdb.html
? doc/src/sgml/app-vacuumdb.html
? doc/src/sgml/appendixes.html
? doc/src/sgml/applevel-consistency.html
? doc/src/sgml/arrays.html
? doc/src/sgml/auth-methods.html
? doc/src/sgml/auth-pg-hba-conf.html
? doc/src/sgml/auth-username-maps.html
? doc/src/sgml/auto-explain.html
? doc/src/sgml/backup-dump.html
? doc/src/sgml/backup-file.html
? doc/src/sgml/backup.html
? doc/src/sgml/biblio.html
? doc/src/sgml/bki-commands.html
? doc/src/sgml/bki-example.html
? doc/src/sgml/bki-format.html
? doc/src/sgml/bki-structure.html
? doc/src/sgml/bki.html
? doc/src/sgml/bookindex.html
? doc/src/sgml/bookindex.sgml
? doc/src/sgml/btree-gin.html
? doc/src/sgml/btree-gist.html
? doc/src/sgml/bug-reporting.html
? doc/src/sgml/catalog-pg-aggregate.html
? doc/src/sgml/catalog-pg-am.html
? doc/src/sgml/catalog-pg-amop.html
? doc/src/sgml/catalog-pg-amproc.html
? doc/src/sgml/catalog-pg-attrdef.html
? doc/src/sgml/catalog-pg-attribute.html
? doc/src/sgml/catalog-pg-auth-members.html
? doc/src/sgml/catalog-pg-authid.html
? doc/src/sgml/catalog-pg-cast.html
? doc/src/sgml/catalog-pg-class.html
? doc/src/sgml/catalog-pg-constraint.html
? doc/src/sgml/catalog-pg-conversion.html
? doc/src/sgml/catalog-pg-database.html
? doc/src/sgml/catalog-pg-depend.html
? doc/src/sgml/catalog-pg-description.html
? doc/src/sgml/catalog-pg-enum.html
? doc/src/sgml/catalog-pg-foreign-data-wrapper.html
? doc/src/sgml/catalog-pg-foreign-server.html
? doc/src/sgml/catalog-pg-index.html
? doc/src/sgml/catalog-pg-inherits.html
? doc/src/sgml/catalog-pg-language.html
? doc/src/sgml/catalog-pg-largeobject.html
? doc/src/sgml/catalog-pg-listener.html
? doc/src/sgml/catalog-pg-namespace.html
? doc/src/sgml/catalog-pg-opclass.html
? doc/src/sgml/catalog-pg-operator.html
? doc/src/sgml/catalog-pg-opfamily.html
? doc/src/sgml/catalog-pg-pltemplate.html
? doc/src/sgml/catalog-pg-proc.html
? doc/src/sgml/catalog-pg-rewrite.html
? doc/src/sgml/catalog-pg-shdepend.html
? doc/src/sgml/catalog-pg-shdescription.html
? doc/src/sgml/catalog-pg-statistic.html
? doc/src/sgml/catalog-pg-tablespace.html
? doc/src/sgml/catalog-pg-trigger.html
? doc/src/sgml/catalog-pg-ts-config-map.html
? doc/src/sgml/catalog-pg-ts-config.html
? doc/src/sgml/catalog-pg-ts-dict.html
? doc/src/sgml/catalog-pg-ts-parser.html
? doc/src/sgml/catalog-pg-ts-template.html
? doc/src/sgml/catalog-pg-type.html
? doc/src/sgml/catalog-pg-user-mapping.html
? doc/src/sgml/catalogs-overview.html
? doc/src/sgml/catalogs.html
? doc/src/sgml/charset.html
? doc/src/sgml/chkpass.html
? doc/src/sgml/citext.html
? doc/src/sgml/client-authentication-problems.html
? doc/src/sgml/client-authentication.html
? doc/src/sgml/client-interfaces.html
? doc/src/sgml/config-setting.html
? doc/src/sgml/connect-estab.html
? doc/src/sgml/continuous-archiving.html
? doc/src/sgml/contrib-dblink-build-sql-delete.html
? doc/src/sgml/contrib-dblink-build-sql-insert.html
? doc/src/sgml/contrib-dblink-build-sql-update.html
? doc/src/sgml/contrib-dblink-cancel-query.html
? doc/src/sgml/contrib-dblink-close.html
? doc/src/sgml/contrib-dblink-connect-u.html
? doc/src/sgml/contrib-dblink-connect.html
? doc/src/sgml/contrib-dblink-disconnect.html
? doc/src/sgml/contrib-dblink-error-message.html
? doc/src/sgml/contrib-dblink-exec.html
? doc/src/sgml/contrib-dblink-fetch.html
? doc/src/sgml/contrib-dblink-get-connections.html
? doc/src/sgml/contrib-dblink-get-pkey.html
? doc/src/sgml/contrib-dblink-get-result.html
? doc/src/sgml/contrib-dblink-is-busy.html
? doc/src/sgml/contrib-dblink-open.html
? doc/src/sgml/contrib-dblink-send-query.html
? doc/src/sgml/contrib-dblink.html
? doc/src/sgml/contrib-spi.html
? doc/src/sgml/contrib.html
? doc/src/sgml/creating-cluster.html
? doc/src/sgml/cube.html
? doc/src/sgml/cvs-tree.html
? doc/src/sgml/cvs.html
? doc/src/sgml/cvsup.html
? doc/src/sgml/database-roles.html
? doc/src/sgml/datatype-binary.html
? doc/src/sgml/datatype-bit.html
? doc/src/sgml/datatype-boolean.html
? doc/src/sgml/datatype-character.html
? doc/src/sgml/datatype-datetime.html
? doc/src/sgml/datatype-enum.html
? doc/src/sgml/datatype-geometric.html
? doc/src/sgml/datatype-money.html
? doc/src/sgml/datatype-net-types.html
? doc/src/sgml/datatype-numeric.html
? doc/src/sgml/datatype-oid.html
? doc/src/sgml/datatype-pseudo.html
? doc/src/sgml/datatype-textsearch.html
? doc/src/sgml/datatype-uuid.html
? doc/src/sgml/datatype-xml.html
? doc/src/sgml/datatype.html
? doc/src/sgml/datetime-appendix.html
? doc/src/sgml/datetime-config-files.html
? doc/src/sgml/datetime-input-rules.html
? doc/src/sgml/datetime-keywords.html
? doc/src/sgml/datetime-units-history.html
? doc/src/sgml/dblink.html
? doc/src/sgml/ddl-alter.html
? doc/src/sgml/ddl-basics.html
? doc/src/sgml/ddl-constraints.html
? doc/src/sgml/ddl-default.html
? doc/src/sgml/ddl-depend.html
? doc/src/sgml/ddl-inherit.html
? doc/src/sgml/ddl-others.html
? doc/src/sgml/ddl-partitioning.html
? doc/src/sgml/ddl-priv.html
? doc/src/sgml/ddl-schemas.html
? doc/src/sgml/ddl-system-columns.html
? doc/src/sgml/ddl.html
? doc/src/sgml/dict-int.html
? doc/src/sgml/dict-xsyn.html
? doc/src/sgml/disk-full.html
? doc/src/sgml/disk-usage.html
? doc/src/sgml/diskusage.html
? doc/src/sgml/dml-delete.html
? doc/src/sgml/dml-insert.html
? doc/src/sgml/dml-update.html
? doc/src/sgml/dml.html
? doc/src/sgml/docguide-authoring.html
? doc/src/sgml/docguide-build.html
? doc/src/sgml/docguide-docbook.html
? doc/src/sgml/docguide-style.html
? doc/src/sgml/docguide-toolsets.html
? doc/src/sgml/docguide.html
? doc/src/sgml/dynamic-trace.html
? doc/src/sgml/earthdistance.html
? doc/src/sgml/ecpg-commands.html
? doc/src/sgml/ecpg-concept.html
? doc/src/sgml/ecpg-connect.html
? doc/src/sgml/ecpg-descriptors.html
? doc/src/sgml/ecpg-develop.html
? doc/src/sgml/ecpg-disconnect.html
? doc/src/sgml/ecpg-dynamic.html
? doc/src/sgml/ecpg-errors.html
? doc/src/sgml/ecpg-informix-compat.html
? doc/src/sgml/ecpg-library.html
? doc/src/sgml/ecpg-pgtypes.html
? doc/src/sgml/ecpg-preproc.html
? doc/src/sgml/ecpg-process.html
? doc/src/sgml/ecpg-set-connection.html
? doc/src/sgml/ecpg-variables.html
? doc/src/sgml/ecpg.html
? doc/src/sgml/encryption-options.html
? doc/src/sgml/errcodes-appendix.html
? doc/src/sgml/error-message-reporting.html
? doc/src/sgml/error-style-guide.html
? doc/src/sgml/executor.html
? doc/src/sgml/explicit-joins.html
? doc/src/sgml/explicit-locking.html
? doc/src/sgml/extend-how.html
? doc/src/sgml/extend-type-system.html
? doc/src/sgml/extend.html
? doc/src/sgml/external-extensions.html
? doc/src/sgml/external-interfaces.html
? doc/src/sgml/external-pl.html
? doc/src/sgml/external-projects.html
? doc/src/sgml/features-sql-standard.html
? doc/src/sgml/features-supported.sgml
? doc/src/sgml/features-unsupported.sgml
? doc/src/sgml/features.html
? doc/src/sgml/functions-admin.html
? doc/src/sgml/functions-aggregate.html
? doc/src/sgml/functions-array.html
? doc/src/sgml/functions-binarystring.html
? doc/src/sgml/functions-bitstring.html
? doc/src/sgml/functions-comparison.html
? doc/src/sgml/functions-comparisons.html
? doc/src/sgml/functions-conditional.html
? doc/src/sgml/functions-datetime.html
? doc/src/sgml/functions-enum.html
? doc/src/sgml/functions-formatting.html
? doc/src/sgml/functions-geometry.html
? doc/src/sgml/functions-info.html
? doc/src/sgml/functions-logical.html
? doc/src/sgml/functions-matching.html
? doc/src/sgml/functions-math.html
? doc/src/sgml/functions-net.html
? doc/src/sgml/functions-sequence.html
? doc/src/sgml/functions-srf.html
? doc/src/sgml/functions-string.html
? doc/src/sgml/functions-subquery.html
? doc/src/sgml/functions-textsearch.html
? doc/src/sgml/functions-trigger.html
? doc/src/sgml/functions-window.html
? doc/src/sgml/functions-xml.html
? doc/src/sgml/functions.html
? doc/src/sgml/fuzzystrmatch.html
? doc/src/sgml/geqo-biblio.html
? doc/src/sgml/geqo-intro.html
? doc/src/sgml/geqo-intro2.html
? doc/src/sgml/geqo-pg-intro.html
? doc/src/sgml/geqo.html
? doc/src/sgml/gin-examples.html
? doc/src/sgml/gin-extensibility.html
? doc/src/sgml/gin-implementation.html
? doc/src/sgml/gin-intro.html
? doc/src/sgml/gin-limit.html
? doc/src/sgml/gin-tips.html
? doc/src/sgml/gin.html
? doc/src/sgml/gist-examples.html
? doc/src/sgml/gist-extensibility.html
? doc/src/sgml/gist-implementation.html
? doc/src/sgml/gist-intro.html
? doc/src/sgml/gist-recovery.html
? doc/src/sgml/gist.html
? doc/src/sgml/high-availability.html
? doc/src/sgml/history.html
? doc/src/sgml/hstore.html
? doc/src/sgml/index-catalog.html
? doc/src/sgml/index-cost-estimation.html
? doc/src/sgml/index-functions.html
? doc/src/sgml/index-locking.html
? doc/src/sgml/index-scanning.html
? doc/src/sgml/index-unique-checks.html
? doc/src/sgml/index.html
? doc/src/sgml/indexam.html
? doc/src/sgml/indexes-bitmap-scans.html
? doc/src/sgml/indexes-examine.html
? doc/src/sgml/indexes-expressional.html
? doc/src/sgml/indexes-intro.html
? doc/src/sgml/indexes-multicolumn.html
? doc/src/sgml/indexes-opclass.html
? doc/src/sgml/indexes-ordering.html
? doc/src/sgml/indexes-partial.html
? doc/src/sgml/indexes-types.html
? doc/src/sgml/indexes-unique.html
? doc/src/sgml/indexes.html
? doc/src/sgml/information-schema.html
? doc/src/sgml/infoschema-administrable-role-authorizations.html
? doc/src/sgml/infoschema-applicable-roles.html
? doc/src/sgml/infoschema-attributes.html
? doc/src/sgml/infoschema-check-constraint-routine-usage.html
? doc/src/sgml/infoschema-check-constraints.html
? doc/src/sgml/infoschema-column-domain-usage.html
? doc/src/sgml/infoschema-column-privileges.html
? doc/src/sgml/infoschema-column-udt-usage.html
? doc/src/sgml/infoschema-columns.html
? doc/src/sgml/infoschema-constraint-column-usage.html
? doc/src/sgml/infoschema-constraint-table-usage.html
? doc/src/sgml/infoschema-data-type-privileges.html
? doc/src/sgml/infoschema-datatypes.html
? doc/src/sgml/infoschema-domain-constraints.html
? doc/src/sgml/infoschema-domain-udt-usage.html
? doc/src/sgml/infoschema-domains.html
? doc/src/sgml/infoschema-element-types.html
? doc/src/sgml/infoschema-enabled-roles.html
? doc/src/sgml/infoschema-foreign-data-wrapper-options.html
? doc/src/sgml/infoschema-foreign-data-wrappers.html
? doc/src/sgml/infoschema-foreign-server-options.html
? doc/src/sgml/infoschema-foreign-servers.html
? doc/src/sgml/infoschema-information-schema-catalog-name.html
? doc/src/sgml/infoschema-key-column-usage.html
? doc/src/sgml/infoschema-parameters.html
? doc/src/sgml/infoschema-referential-constraints.html
? doc/src/sgml/infoschema-role-column-grants.html
? doc/src/sgml/infoschema-role-routine-grants.html
? doc/src/sgml/infoschema-role-table-grants.html
? doc/src/sgml/infoschema-role-usage-grants.html
? doc/src/sgml/infoschema-routine-privileges.html
? doc/src/sgml/infoschema-routines.html
? doc/src/sgml/infoschema-schema.html
? doc/src/sgml/infoschema-schemata.html
? doc/src/sgml/infoschema-sequences.html
? doc/src/sgml/infoschema-sql-features.html
? doc/src/sgml/infoschema-sql-implementation-info.html
? doc/src/sgml/infoschema-sql-languages.html
? doc/src/sgml/infoschema-sql-packages.html
? doc/src/sgml/infoschema-sql-parts.html
? doc/src/sgml/infoschema-sql-sizing-profiles.html
? doc/src/sgml/infoschema-sql-sizing.html
? doc/src/sgml/infoschema-table-constraints.html
? doc/src/sgml/infoschema-table-privileges.html
? doc/src/sgml/infoschema-tables.html
? doc/src/sgml/infoschema-triggers.html
? doc/src/sgml/infoschema-usage-privileges.html
? doc/src/sgml/infoschema-user-mapping-options.html
? doc/src/sgml/infoschema-user-mappings.html
? doc/src/sgml/infoschema-view-column-usage.html
? doc/src/sgml/infoschema-view-routine-usage.html
? doc/src/sgml/infoschema-view-table-usage.html
? doc/src/sgml/infoschema-views.html
? doc/src/sgml/install-getsource.html
? doc/src/sgml/install-post.html
? doc/src/sgml/install-procedure.html
? doc/src/sgml/install-requirements.html
? doc/src/sgml/install-short.html
? doc/src/sgml/install-upgrading.html
? doc/src/sgml/install-win32-full.html
? doc/src/sgml/install-win32-libpq.html
? doc/src/sgml/install-win32.html
? doc/src/sgml/installation-platform-notes.html
? doc/src/sgml/installation.html
? doc/src/sgml/intagg.html
? doc/src/sgml/intarray.html
? doc/src/sgml/internals.html
? doc/src/sgml/intro-whatis.html
? doc/src/sgml/isn.html
? doc/src/sgml/kernel-resources.html
? doc/src/sgml/largeobjects.html
? doc/src/sgml/libpq-async.html
? doc/src/sgml/libpq-build.html
? doc/src/sgml/libpq-cancel.html
? doc/src/sgml/libpq-connect.html
? doc/src/sgml/libpq-control.html
? doc/src/sgml/libpq-copy.html
? doc/src/sgml/libpq-envars.html
? doc/src/sgml/libpq-events.html
? doc/src/sgml/libpq-example.html
? doc/src/sgml/libpq-exec.html
? doc/src/sgml/libpq-fastpath.html
? doc/src/sgml/libpq-ldap.html
? doc/src/sgml/libpq-misc.html
? doc/src/sgml/libpq-notice-processing.html
? doc/src/sgml/libpq-notify.html
? doc/src/sgml/libpq-pgpass.html
? doc/src/sgml/libpq-pgservice.html
? doc/src/sgml/libpq-ssl.html
? doc/src/sgml/libpq-status.html
? doc/src/sgml/libpq-threading.html
? doc/src/sgml/libpq.html
? doc/src/sgml/lo-examplesect.html
? doc/src/sgml/lo-funcs.html
? doc/src/sgml/lo-implementation.html
? doc/src/sgml/lo-interfaces.html
? doc/src/sgml/lo-intro.html
? doc/src/sgml/lo.html
? doc/src/sgml/locale.html
? doc/src/sgml/locking-indexes.html
? doc/src/sgml/logfile-maintenance.html
? doc/src/sgml/ltree.html
? doc/src/sgml/maintenance.html
? doc/src/sgml/manage-ag-config.html
? doc/src/sgml/manage-ag-createdb.html
? doc/src/sgml/manage-ag-dropdb.html
? doc/src/sgml/manage-ag-overview.html
? doc/src/sgml/manage-ag-tablespaces.html
? doc/src/sgml/manage-ag-templatedbs.html
? doc/src/sgml/managing-databases.html
? doc/src/sgml/migration.html
? doc/src/sgml/monitoring-locks.html
? doc/src/sgml/monitoring-ps.html
? doc/src/sgml/monitoring-stats.html
? doc/src/sgml/monitoring.html
? doc/src/sgml/multibyte.html
? doc/src/sgml/mvcc-intro.html
? doc/src/sgml/mvcc.html
? doc/src/sgml/nls-programmer.html
? doc/src/sgml/nls-translator.html
? doc/src/sgml/nls.html
? doc/src/sgml/notation.html
? doc/src/sgml/oid2name.html
? doc/src/sgml/overview.html
? doc/src/sgml/pageinspect.html
? doc/src/sgml/parser-stage.html
? doc/src/sgml/performance-tips.html
? doc/src/sgml/perm-functions.html
? doc/src/sgml/pgbench.html
? doc/src/sgml/pgbuffercache.html
? doc/src/sgml/pgcrypto.html
? doc/src/sgml/pgfreespacemap.html
? doc/src/sgml/pgrowlocks.html
? doc/src/sgml/pgstandby.html
? doc/src/sgml/pgstatstatements.html
? doc/src/sgml/pgstattuple.html
? doc/src/sgml/pgtrgm.html
? doc/src/sgml/planner-optimizer.html
? doc/src/sgml/planner-stats-details.html
? doc/src/sgml/planner-stats.html
? doc/src/sgml/plhandler.html
? doc/src/sgml/plperl-data.html
? doc/src/sgml/plperl-database.html
? doc/src/sgml/plperl-funcs.html
? doc/src/sgml/plperl-global.html
? doc/src/sgml/plperl-missing.html
? doc/src/sgml/plperl-triggers.html
? doc/src/sgml/plperl-trusted.html
? doc/src/sgml/plperl.html
? doc/src/sgml/plpgsql-control-structures.html
? doc/src/sgml/plpgsql-cursors.html
? doc/src/sgml/plpgsql-declarations.html
? doc/src/sgml/plpgsql-development-tips.html
? doc/src/sgml/plpgsql-errors-and-messages.html
? doc/src/sgml/plpgsql-expressions.html
? doc/src/sgml/plpgsql-implementation.html
? doc/src/sgml/plpgsql-overview.html
? doc/src/sgml/plpgsql-porting.html
? doc/src/sgml/plpgsql-statements.html
? doc/src/sgml/plpgsql-structure.html
? doc/src/sgml/plpgsql-trigger.html
? doc/src/sgml/plpgsql.html
? doc/src/sgml/plpython-database.html
? doc/src/sgml/plpython-funcs.html
? doc/src/sgml/plpython-trigger.html
? doc/src/sgml/plpython.html
? doc/src/sgml/pltcl-data.html
? doc/src/sgml/pltcl-dbaccess.html
? doc/src/sgml/pltcl-functions.html
? doc/src/sgml/pltcl-global.html
? doc/src/sgml/pltcl-overview.html
? doc/src/sgml/pltcl-procnames.html
? doc/src/sgml/pltcl-trigger.html
? doc/src/sgml/pltcl-unknown.html
? doc/src/sgml/pltcl.html
? doc/src/sgml/populate.html
? doc/src/sgml/postgres-user.html
? doc/src/sgml/preface.html
? doc/src/sgml/preventing-server-spoofing.html
? doc/src/sgml/privileges.html
? doc/src/sgml/protocol-changes.html
? doc/src/sgml/protocol-error-fields.html
? doc/src/sgml/protocol-flow.html
? doc/src/sgml/protocol-message-formats.html
? doc/src/sgml/protocol-message-types.html
? doc/src/sgml/protocol-overview.html
? doc/src/sgml/protocol.html
? doc/src/sgml/queries-limit.html
? doc/src/sgml/queries-order.html
? doc/src/sgml/queries-overview.html
? doc/src/sgml/queries-select-lists.html
? doc/src/sgml/queries-table-expressions.html
? doc/src/sgml/queries-union.html
? doc/src/sgml/queries-values.html
? doc/src/sgml/queries-with.html
? doc/src/sgml/queries.html
? doc/src/sgml/query-path.html
? doc/src/sgml/querytree.html
? doc/src/sgml/reference-client.html
? doc/src/sgml/reference-server.html
? doc/src/sgml/reference.html
? doc/src/sgml/regress-coverage.html
? doc/src/sgml/regress-evaluation.html
? doc/src/sgml/regress-run.html
? doc/src/sgml/regress-variant.html
? doc/src/sgml/regress.html
? doc/src/sgml/release-0-01.html
? doc/src/sgml/release-0-02.html
? doc/src/sgml/release-0-03.html
? doc/src/sgml/release-1-0.html
? doc/src/sgml/release-1-01.html
? doc/src/sgml/release-1-02.html
? doc/src/sgml/release-1-09.html
? doc/src/sgml/release-6-0.html
? doc/src/sgml/release-6-1-1.html
? doc/src/sgml/release-6-1.html
? doc/src/sgml/release-6-2-1.html
? doc/src/sgml/release-6-2.html
? doc/src/sgml/release-6-3-1.html
? doc/src/sgml/release-6-3-2.html
? doc/src/sgml/release-6-3.html
? doc/src/sgml/release-6-4-1.html
? doc/src/sgml/release-6-4-2.html
? doc/src/sgml/release-6-4.html
? doc/src/sgml/release-6-5-1.html
? doc/src/sgml/release-6-5-2.html
? doc/src/sgml/release-6-5-3.html
? doc/src/sgml/release-6-5.html
? doc/src/sgml/release-7-0-1.html
? doc/src/sgml/release-7-0-2.html
? doc/src/sgml/release-7-0-3.html
? doc/src/sgml/release-7-0.html
? doc/src/sgml/release-7-1-1.html
? doc/src/sgml/release-7-1-2.html
? doc/src/sgml/release-7-1-3.html
? doc/src/sgml/release-7-1.html
? doc/src/sgml/release-7-2-1.html
? doc/src/sgml/release-7-2-2.html
? doc/src/sgml/release-7-2-3.html
? doc/src/sgml/release-7-2-4.html
? doc/src/sgml/release-7-2-5.html
? doc/src/sgml/release-7-2-6.html
? doc/src/sgml/release-7-2-7.html
? doc/src/sgml/release-7-2-8.html
? doc/src/sgml/release-7-2.html
? doc/src/sgml/release-7-3-1.html
? doc/src/sgml/release-7-3-10.html
? doc/src/sgml/release-7-3-11.html
? doc/src/sgml/release-7-3-12.html
? doc/src/sgml/release-7-3-13.html
? doc/src/sgml/release-7-3-14.html
? doc/src/sgml/release-7-3-15.html
? doc/src/sgml/release-7-3-16.html
? doc/src/sgml/release-7-3-17.html
? doc/src/sgml/release-7-3-18.html
? doc/src/sgml/release-7-3-19.html
? doc/src/sgml/release-7-3-2.html
? doc/src/sgml/release-7-3-20.html
? doc/src/sgml/release-7-3-21.html
? doc/src/sgml/release-7-3-3.html
? doc/src/sgml/release-7-3-4.html
? doc/src/sgml/release-7-3-5.html
? doc/src/sgml/release-7-3-6.html
? doc/src/sgml/release-7-3-7.html
? doc/src/sgml/release-7-3-8.html
? doc/src/sgml/release-7-3-9.html
? doc/src/sgml/release-7-3.html
? doc/src/sgml/release-7-4-1.html
? doc/src/sgml/release-7-4-10.html
? doc/src/sgml/release-7-4-11.html
? doc/src/sgml/release-7-4-12.html
? doc/src/sgml/release-7-4-13.html
? doc/src/sgml/release-7-4-14.html
? doc/src/sgml/release-7-4-15.html
? doc/src/sgml/release-7-4-16.html
? doc/src/sgml/release-7-4-17.html
? doc/src/sgml/release-7-4-18.html
? doc/src/sgml/release-7-4-19.html
? doc/src/sgml/release-7-4-2.html
? doc/src/sgml/release-7-4-20.html
? doc/src/sgml/release-7-4-21.html
? doc/src/sgml/release-7-4-22.html
? doc/src/sgml/release-7-4-23.html
? doc/src/sgml/release-7-4-24.html
? doc/src/sgml/release-7-4-25.html
? doc/src/sgml/release-7-4-3.html
? doc/src/sgml/release-7-4-4.html
? doc/src/sgml/release-7-4-5.html
? doc/src/sgml/release-7-4-6.html
? doc/src/sgml/release-7-4-7.html
? doc/src/sgml/release-7-4-8.html
? doc/src/sgml/release-7-4-9.html
? doc/src/sgml/release-7-4.html
? doc/src/sgml/release-8-0-1.html
? doc/src/sgml/release-8-0-10.html
? doc/src/sgml/release-8-0-11.html
? doc/src/sgml/release-8-0-12.html
? doc/src/sgml/release-8-0-13.html
? doc/src/sgml/release-8-0-14.html
? doc/src/sgml/release-8-0-15.html
? doc/src/sgml/release-8-0-16.html
? doc/src/sgml/release-8-0-17.html
? doc/src/sgml/release-8-0-18.html
? doc/src/sgml/release-8-0-19.html
? doc/src/sgml/release-8-0-2.html
? doc/src/sgml/release-8-0-20.html
? doc/src/sgml/release-8-0-21.html
? doc/src/sgml/release-8-0-3.html
? doc/src/sgml/release-8-0-4.html
? doc/src/sgml/release-8-0-5.html
? doc/src/sgml/release-8-0-6.html
? doc/src/sgml/release-8-0-7.html
? doc/src/sgml/release-8-0-8.html
? doc/src/sgml/release-8-0-9.html
? doc/src/sgml/release-8-0.html
? doc/src/sgml/release-8-1-1.html
? doc/src/sgml/release-8-1-10.html
? doc/src/sgml/release-8-1-11.html
? doc/src/sgml/release-8-1-12.html
? doc/src/sgml/release-8-1-13.html
? doc/src/sgml/release-8-1-14.html
? doc/src/sgml/release-8-1-15.html
? doc/src/sgml/release-8-1-16.html
? doc/src/sgml/release-8-1-17.html
? doc/src/sgml/release-8-1-2.html
? doc/src/sgml/release-8-1-3.html
? doc/src/sgml/release-8-1-4.html
? doc/src/sgml/release-8-1-5.html
? doc/src/sgml/release-8-1-6.html
? doc/src/sgml/release-8-1-7.html
? doc/src/sgml/release-8-1-8.html
? doc/src/sgml/release-8-1-9.html
? doc/src/sgml/release-8-1.html
? doc/src/sgml/release-8-2-1.html
? doc/src/sgml/release-8-2-10.html
? doc/src/sgml/release-8-2-11.html
? doc/src/sgml/release-8-2-12.html
? doc/src/sgml/release-8-2-13.html
? doc/src/sgml/release-8-2-2.html
? doc/src/sgml/release-8-2-3.html
? doc/src/sgml/release-8-2-4.html
? doc/src/sgml/release-8-2-5.html
? doc/src/sgml/release-8-2-6.html
? doc/src/sgml/release-8-2-7.html
? doc/src/sgml/release-8-2-8.html
? doc/src/sgml/release-8-2-9.html
? doc/src/sgml/release-8-2.html
? doc/src/sgml/release-8-3-1.html
? doc/src/sgml/release-8-3-2.html
? doc/src/sgml/release-8-3-3.html
? doc/src/sgml/release-8-3-4.html
? doc/src/sgml/release-8-3-5.html
? doc/src/sgml/release-8-3-6.html
? doc/src/sgml/release-8-3-7.html
? doc/src/sgml/release-8-3.html
? doc/src/sgml/release-8-4.html
? doc/src/sgml/release.html
? doc/src/sgml/resources.html
? doc/src/sgml/role-attributes.html
? doc/src/sgml/role-membership.html
? doc/src/sgml/routine-reindex.html
? doc/src/sgml/routine-vacuuming.html
? doc/src/sgml/row-estimation-examples.html
? doc/src/sgml/rowtypes.html
? doc/src/sgml/rsync.html
? doc/src/sgml/rule-system.html
? doc/src/sgml/rules-privileges.html
? doc/src/sgml/rules-status.html
? doc/src/sgml/rules-triggers.html
? doc/src/sgml/rules-update.html
? doc/src/sgml/rules-views.html
? doc/src/sgml/rules.html
? doc/src/sgml/runtime-config-autovacuum.html
? doc/src/sgml/runtime-config-client.html
? doc/src/sgml/runtime-config-compatible.html
? doc/src/sgml/runtime-config-connection.html
? doc/src/sgml/runtime-config-custom.html
? doc/src/sgml/runtime-config-developer.html
? doc/src/sgml/runtime-config-file-locations.html
? doc/src/sgml/runtime-config-locks.html
? doc/src/sgml/runtime-config-logging.html
? doc/src/sgml/runtime-config-preset.html
? doc/src/sgml/runtime-config-query.html
? doc/src/sgml/runtime-config-resource.html
? doc/src/sgml/runtime-config-short.html
? doc/src/sgml/runtime-config-statistics.html
? doc/src/sgml/runtime-config-wal.html
? doc/src/sgml/runtime-config.html
? doc/src/sgml/runtime.html
? doc/src/sgml/seg.html
? doc/src/sgml/server-programming.html
? doc/src/sgml/server-shutdown.html
? doc/src/sgml/server-start.html
? doc/src/sgml/source-format.html
? doc/src/sgml/source.html
? doc/src/sgml/spi-examples.html
? doc/src/sgml/spi-interface-support.html
? doc/src/sgml/spi-interface.html
? doc/src/sgml/spi-memory.html
? doc/src/sgml/spi-realloc.html
? doc/src/sgml/spi-spi-connect.html
? doc/src/sgml/spi-spi-copytuple.html
? doc/src/sgml/spi-spi-cursor-close.html
? doc/src/sgml/spi-spi-cursor-fetch.html
? doc/src/sgml/spi-spi-cursor-find.html
? doc/src/sgml/spi-spi-cursor-move.html
? doc/src/sgml/spi-spi-cursor-open-with-args.html
? doc/src/sgml/spi-spi-cursor-open.html
? doc/src/sgml/spi-spi-exec.html
? doc/src/sgml/spi-spi-execp.html
? doc/src/sgml/spi-spi-execute-plan.html
? doc/src/sgml/spi-spi-execute-with-args.html
? doc/src/sgml/spi-spi-execute.html
? doc/src/sgml/spi-spi-finish.html
? doc/src/sgml/spi-spi-fname.html
? doc/src/sgml/spi-spi-fnumber.html
? doc/src/sgml/spi-spi-freeplan.html
? doc/src/sgml/spi-spi-freetuple.html
? doc/src/sgml/spi-spi-freetupletable.html
? doc/src/sgml/spi-spi-getargcount.html
? doc/src/sgml/spi-spi-getargtypeid.html
? doc/src/sgml/spi-spi-getbinval.html
? doc/src/sgml/spi-spi-getnspname.html
? doc/src/sgml/spi-spi-getrelname.html
? doc/src/sgml/spi-spi-gettype.html
? doc/src/sgml/spi-spi-gettypeid.html
? doc/src/sgml/spi-spi-getvalue.html
? doc/src/sgml/spi-spi-is-cursor-plan.html
? doc/src/sgml/spi-spi-modifytuple.html
? doc/src/sgml/spi-spi-palloc.html
? doc/src/sgml/spi-spi-pfree.html
? doc/src/sgml/spi-spi-pop.html
? doc/src/sgml/spi-spi-prepare-cursor.html
? doc/src/sgml/spi-spi-prepare.html
? doc/src/sgml/spi-spi-push.html
? doc/src/sgml/spi-spi-returntuple.html
? doc/src/sgml/spi-spi-saveplan.html
? doc/src/sgml/spi-spi-scroll-cursor-fetch.html
? doc/src/sgml/spi-spi-scroll-cursor-move.html
? doc/src/sgml/spi-visibility.html
? doc/src/sgml/spi.html
? doc/src/sgml/sql-abort.html
? doc/src/sgml/sql-alteraggregate.html
? doc/src/sgml/sql-alterconversion.html
? doc/src/sgml/sql-alterdatabase.html
? doc/src/sgml/sql-alterdomain.html
? doc/src/sgml/sql-alterforeigndatawrapper.html
? doc/src/sgml/sql-alterfunction.html
? doc/src/sgml/sql-altergroup.html
? doc/src/sgml/sql-alterindex.html
? doc/src/sgml/sql-alterlanguage.html
? doc/src/sgml/sql-alteropclass.html
? doc/src/sgml/sql-alteroperator.html
? doc/src/sgml/sql-alteropfamily.html
? doc/src/sgml/sql-alterrole.html
? doc/src/sgml/sql-alterschema.html
? doc/src/sgml/sql-altersequence.html
? doc/src/sgml/sql-alterserver.html
? doc/src/sgml/sql-altertable.html
? doc/src/sgml/sql-altertablespace.html
? doc/src/sgml/sql-altertrigger.html
? doc/src/sgml/sql-altertsconfig.html
? doc/src/sgml/sql-altertsdictionary.html
? doc/src/sgml/sql-altertsparser.html
? doc/src/sgml/sql-altertstemplate.html
? doc/src/sgml/sql-altertype.html
? doc/src/sgml/sql-alteruser.html
? doc/src/sgml/sql-alterusermapping.html
? doc/src/sgml/sql-alterview.html
? doc/src/sgml/sql-analyze.html
? doc/src/sgml/sql-begin.html
? doc/src/sgml/sql-checkpoint.html
? doc/src/sgml/sql-close.html
? doc/src/sgml/sql-cluster.html
? doc/src/sgml/sql-commands.html
? doc/src/sgml/sql-comment.html
? doc/src/sgml/sql-commit-prepared.html
? doc/src/sgml/sql-commit.html
? doc/src/sgml/sql-copy.html
? doc/src/sgml/sql-createaggregate.html
? doc/src/sgml/sql-createcast.html
? doc/src/sgml/sql-createconstraint.html
? doc/src/sgml/sql-createconversion.html
? doc/src/sgml/sql-createdatabase.html
? doc/src/sgml/sql-createdomain.html
? doc/src/sgml/sql-createforeigndatawrapper.html
? doc/src/sgml/sql-createfunction.html
? doc/src/sgml/sql-creategroup.html
? doc/src/sgml/sql-createindex.html
? doc/src/sgml/sql-createlanguage.html
? doc/src/sgml/sql-createopclass.html
? doc/src/sgml/sql-createoperator.html
? doc/src/sgml/sql-createopfamily.html
? doc/src/sgml/sql-createrole.html
? doc/src/sgml/sql-createrule.html
? doc/src/sgml/sql-createschema.html
? doc/src/sgml/sql-createsequence.html
? doc/src/sgml/sql-createserver.html
? doc/src/sgml/sql-createtable.html
? doc/src/sgml/sql-createtableas.html
? doc/src/sgml/sql-createtablespace.html
? doc/src/sgml/sql-createtrigger.html
? doc/src/sgml/sql-createtsconfig.html
? doc/src/sgml/sql-createtsdictionary.html
? doc/src/sgml/sql-createtsparser.html
? doc/src/sgml/sql-createtstemplate.html
? doc/src/sgml/sql-createtype.html
? doc/src/sgml/sql-createuser.html
? doc/src/sgml/sql-createusermapping.html
? doc/src/sgml/sql-createview.html
? doc/src/sgml/sql-deallocate.html
? doc/src/sgml/sql-declare.html
? doc/src/sgml/sql-delete.html
? doc/src/sgml/sql-discard.html
? doc/src/sgml/sql-drop-owned.html
? doc/src/sgml/sql-dropaggregate.html
? doc/src/sgml/sql-dropcast.html
? doc/src/sgml/sql-dropconversion.html
? doc/src/sgml/sql-dropdatabase.html
? doc/src/sgml/sql-dropdomain.html
? doc/src/sgml/sql-dropforeigndatawrapper.html
? doc/src/sgml/sql-dropfunction.html
? doc/src/sgml/sql-dropgroup.html
? doc/src/sgml/sql-dropindex.html
? doc/src/sgml/sql-droplanguage.html
? doc/src/sgml/sql-dropopclass.html
? doc/src/sgml/sql-dropoperator.html
? doc/src/sgml/sql-dropopfamily.html
? doc/src/sgml/sql-droprole.html
? doc/src/sgml/sql-droprule.html
? doc/src/sgml/sql-dropschema.html
? doc/src/sgml/sql-dropsequence.html
? doc/src/sgml/sql-dropserver.html
? doc/src/sgml/sql-droptable.html
? doc/src/sgml/sql-droptablespace.html
? doc/src/sgml/sql-droptrigger.html
? doc/src/sgml/sql-droptsconfig.html
? doc/src/sgml/sql-droptsdictionary.html
? doc/src/sgml/sql-droptsparser.html
? doc/src/sgml/sql-droptstemplate.html
? doc/src/sgml/sql-droptype.html
? doc/src/sgml/sql-dropuser.html
? doc/src/sgml/sql-dropusermapping.html
? doc/src/sgml/sql-dropview.html
? doc/src/sgml/sql-end.html
? doc/src/sgml/sql-execute.html
? doc/src/sgml/sql-explain.html
? doc/src/sgml/sql-expressions.html
? doc/src/sgml/sql-fetch.html
? doc/src/sgml/sql-grant.html
? doc/src/sgml/sql-insert.html
? doc/src/sgml/sql-keywords-appendix.html
? doc/src/sgml/sql-listen.html
? doc/src/sgml/sql-load.html
? doc/src/sgml/sql-lock.html
? doc/src/sgml/sql-move.html
? doc/src/sgml/sql-notify.html
? doc/src/sgml/sql-prepare-transaction.html
? doc/src/sgml/sql-prepare.html
? doc/src/sgml/sql-reassign-owned.html
? doc/src/sgml/sql-reindex.html
? doc/src/sgml/sql-release-savepoint.html
? doc/src/sgml/sql-reset.html
? doc/src/sgml/sql-revoke.html
? doc/src/sgml/sql-rollback-prepared.html
? doc/src/sgml/sql-rollback-to.html
? doc/src/sgml/sql-rollback.html
? doc/src/sgml/sql-savepoint.html
? doc/src/sgml/sql-select.html
? doc/src/sgml/sql-selectinto.html
? doc/src/sgml/sql-set-constraints.html
? doc/src/sgml/sql-set-role.html
? doc/src/sgml/sql-set-session-authorization.html
? doc/src/sgml/sql-set-transaction.html
? doc/src/sgml/sql-set.html
? doc/src/sgml/sql-show.html
? doc/src/sgml/sql-start-transaction.html
? doc/src/sgml/sql-syntax-lexical.html
? doc/src/sgml/sql-syntax.html
? doc/src/sgml/sql-truncate.html
? doc/src/sgml/sql-unlisten.html
? doc/src/sgml/sql-update.html
? doc/src/sgml/sql-vacuum.html
? doc/src/sgml/sql-values.html
? doc/src/sgml/sql.html
? doc/src/sgml/ssh-tunnels.html
? doc/src/sgml/ssl-tcp.html
? doc/src/sgml/sslinfo.html
? doc/src/sgml/storage-file-layout.html
? doc/src/sgml/storage-fsm.html
? doc/src/sgml/storage-page-layout.html
? doc/src/sgml/storage-toast.html
? doc/src/sgml/storage.html
? doc/src/sgml/supported-platforms.html
? doc/src/sgml/tablefunc.html
? doc/src/sgml/test-parser.html
? doc/src/sgml/textsearch-configuration.html
? doc/src/sgml/textsearch-controls.html
? doc/src/sgml/textsearch-debugging.html
? doc/src/sgml/textsearch-dictionaries.html
? doc/src/sgml/textsearch-features.html
? doc/src/sgml/textsearch-indexes.html
? doc/src/sgml/textsearch-intro.html
? doc/src/sgml/textsearch-limitations.html
? doc/src/sgml/textsearch-migration.html
? doc/src/sgml/textsearch-parsers.html
? doc/src/sgml/textsearch-psql.html
? doc/src/sgml/textsearch-tables.html
? doc/src/sgml/textsearch.html
? doc/src/sgml/transaction-iso.html
? doc/src/sgml/trigger-datachanges.html
? doc/src/sgml/trigger-definition.html
? doc/src/sgml/trigger-example.html
? doc/src/sgml/trigger-interface.html
? doc/src/sgml/triggers.html
? doc/src/sgml/tsearch2.html
? doc/src/sgml/tutorial-accessdb.html
? doc/src/sgml/tutorial-advanced-intro.html
? doc/src/sgml/tutorial-advanced.html
? doc/src/sgml/tutorial-agg.html
? doc/src/sgml/tutorial-arch.html
? doc/src/sgml/tutorial-concepts.html
? doc/src/sgml/tutorial-conclusion.html
? doc/src/sgml/tutorial-createdb.html
? doc/src/sgml/tutorial-delete.html
? doc/src/sgml/tutorial-fk.html
? doc/src/sgml/tutorial-inheritance.html
? doc/src/sgml/tutorial-install.html
? doc/src/sgml/tutorial-join.html
? doc/src/sgml/tutorial-populate.html
? doc/src/sgml/tutorial-select.html
? doc/src/sgml/tutorial-sql-intro.html
? doc/src/sgml/tutorial-sql.html
? doc/src/sgml/tutorial-start.html
? doc/src/sgml/tutorial-table.html
? doc/src/sgml/tutorial-transactions.html
? doc/src/sgml/tutorial-update.html
? doc/src/sgml/tutorial-views.html
? doc/src/sgml/tutorial-window.html
? doc/src/sgml/tutorial.html
? doc/src/sgml/typeconv-func.html
? doc/src/sgml/typeconv-oper.html
? doc/src/sgml/typeconv-overview.html
? doc/src/sgml/typeconv-query.html
? doc/src/sgml/typeconv-union-case.html
? doc/src/sgml/typeconv.html
? doc/src/sgml/unsupported-features-sql-standard.html
? doc/src/sgml/user-manag.html
? doc/src/sgml/using-explain.html
? doc/src/sgml/uuid-ossp.html
? doc/src/sgml/vacuumlo.html
? doc/src/sgml/version.sgml
? doc/src/sgml/view-pg-cursors.html
? doc/src/sgml/view-pg-group.html
? doc/src/sgml/view-pg-indexes.html
? doc/src/sgml/view-pg-locks.html
? doc/src/sgml/view-pg-prepared-statements.html
? doc/src/sgml/view-pg-prepared-xacts.html
? doc/src/sgml/view-pg-roles.html
? doc/src/sgml/view-pg-rules.html
? doc/src/sgml/view-pg-settings.html
? doc/src/sgml/view-pg-shadow.html
? doc/src/sgml/view-pg-stats.html
? doc/src/sgml/view-pg-tables.html
? doc/src/sgml/view-pg-timezone-abbrevs.html
? doc/src/sgml/view-pg-timezone-names.html
? doc/src/sgml/view-pg-user-mappings.html
? doc/src/sgml/view-pg-user.html
? doc/src/sgml/view-pg-views.html
? doc/src/sgml/views-overview.html
? doc/src/sgml/wal-async-commit.html
? doc/src/sgml/wal-configuration.html
? doc/src/sgml/wal-internals.html
? doc/src/sgml/wal-intro.html
? doc/src/sgml/wal-reliability.html
? doc/src/sgml/wal.html
? doc/src/sgml/warm-standby.html
? doc/src/sgml/xaggr.html
? doc/src/sgml/xfunc-c.html
? doc/src/sgml/xfunc-internal.html
? doc/src/sgml/xfunc-overload.html
? doc/src/sgml/xfunc-pl.html
? doc/src/sgml/xfunc-sql.html
? doc/src/sgml/xfunc-volatility.html
? doc/src/sgml/xfunc.html
? doc/src/sgml/xindex.html
? doc/src/sgml/xml2.html
? doc/src/sgml/xoper-optimization.html
? doc/src/sgml/xoper.html
? doc/src/sgml/xplang-install.html
? doc/src/sgml/xplang.html
? doc/src/sgml/xtypes.html
? src/Makefile.global
? src/backend/postgres
? src/backend/catalog/postgres.bki
? src/backend/catalog/postgres.description
? src/backend/catalog/postgres.shdescription
? src/backend/snowball/snowball_create.sql
? src/backend/utils/probes.h
? src/backend/utils/mb/conversion_procs/conversion_create.sql
? src/bin/initdb/initdb
? src/bin/pg_config/pg_config
? src/bin/pg_controldata/pg_controldata
? src/bin/pg_ctl/pg_ctl
? src/bin/pg_dump/pg_dump
? src/bin/pg_dump/pg_dumpall
? src/bin/pg_dump/pg_restore
? src/bin/pg_resetxlog/pg_resetxlog
? src/bin/psql/psql
? src/bin/scripts/clusterdb
? src/bin/scripts/createdb
? src/bin/scripts/createlang
? src/bin/scripts/createuser
? src/bin/scripts/dropdb
? src/bin/scripts/droplang
? src/bin/scripts/dropuser
? src/bin/scripts/reindexdb
? src/bin/scripts/vacuumdb
? src/include/pg_config.h
? src/include/stamp-h
? src/interfaces/ecpg/compatlib/exports.list
? src/interfaces/ecpg/compatlib/libecpg_compat.so.3.1
? src/interfaces/ecpg/ecpglib/exports.list
? src/interfaces/ecpg/ecpglib/libecpg.so.6.1
? src/interfaces/ecpg/include/ecpg_config.h
? src/interfaces/ecpg/include/stamp-h
? src/interfaces/ecpg/pgtypeslib/exports.list
? src/interfaces/ecpg/pgtypeslib/libpgtypes.so.3.1
? src/interfaces/ecpg/preproc/ecpg
? src/interfaces/libpq/exports.list
? src/interfaces/libpq/libpq.so.5.2
? src/port/pg_config_paths.h
? src/test/regress/pg_regress
? src/test/regress/testtablespace
? src/timezone/zic
Index: contrib/pg_standby/pg_standby.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pg_standby/pg_standby.c,v
retrieving revision 1.21
diff -c -r1.21 pg_standby.c
*** contrib/pg_standby/pg_standby.c 26 Mar 2009 22:29:13 -0000 1.21
--- contrib/pg_standby/pg_standby.c 8 Apr 2009 01:07:36 -0000
***************
*** 52,58 ****
int keepfiles = 0; /* number of WAL files to keep, 0 keep all */
int maxretries = 3; /* number of retries on restore command */
bool debug = false; /* are we debugging? */
- bool triggered = false; /* have we been triggered? */
bool need_cleanup = false; /* do we need to remove files from
* archive? */
--- 52,57 ----
***************
*** 69,74 ****
--- 68,99 ----
char exclusiveCleanupFileName[MAXPGPATH]; /* the file we need to
* get from archive */
+ /*
+ * Two types of failover are supported (smart and fast failover).
+ *
+ * The content of the trigger file determines the type of failover.
+ * If the trigger file containing "smart" exists, smart failover is chosen;
+ * pg_standby acts as cp or ln command itself, and recovery waits to
+ * finish until all the available WAL files are applied. It's
+ * guaranteed that no "available" commit transactions are lost. But
+ * it might take some times before finishing recovery.
+ *
+ * On the other hand, the existence of the trigger file with "fast"
+ * causes recovery to end immediately even if the available WAL files
+ * remain. So, some transactions might be lost.
+ *
+ * An empty trigger file performs smart failover.
+ *
+ * Fast failover is triggered by the signal (SIGUSR1 or SIGINT).
+ *
+ * A timeout causes smart failover.
+ */
+ #define NoFailover 0
+ #define SmartFailover 1
+ #define FastFailover 2
+
+ static int Failover = NoFailover;
+
#define RESTORE_COMMAND_COPY 0
#define RESTORE_COMMAND_LINK 1
int restoreCommandType;
***************
*** 108,114 ****
*
* As an example, and probably the common case, we use either
* cp/ln commands on *nix, or copy/move command on Windows.
- *
*/
static void
CustomizableInitialize(void)
--- 133,138 ----
***************
*** 357,363 ****
static bool
CheckForExternalTrigger(void)
{
! int rc;
/*
* Look for a trigger file, if that option has been selected
--- 381,388 ----
static bool
CheckForExternalTrigger(void)
{
! char buf[32];
! FILE *fd;
/*
* Look for a trigger file, if that option has been selected
***************
*** 365,374 ****
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (triggerPath && stat(triggerPath, &stat_buf) == 0)
{
! fprintf(stderr, "trigger file found\n");
fflush(stderr);
/*
* If trigger file found, we *must* delete it. Here's why: When
--- 390,439 ----
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (!triggerPath || stat(triggerPath, &stat_buf) != 0)
! return false;
!
! /*
! * An empty trigger file performs smart failover
! */
! if (stat_buf.st_size == 0)
{
! Failover = SmartFailover;
! fprintf(stderr, "trigger file found: smart failover\n");
fflush(stderr);
+ return true;
+ }
+
+ if ((fd = fopen(triggerPath, "r")) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not open \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ return false;
+ }
+
+ if (fgets(buf, sizeof(buf), fd) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not read \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ fclose(fd);
+ return false;
+ }
+
+ fclose(fd);
+
+ if (strspn(buf, "smart") == 5 && strncmp(buf, "smart", 5) == 0)
+ {
+ Failover = SmartFailover;
+ fprintf(stderr, "trigger file found: smart failover\n");
+ fflush(stderr);
+ return true;
+ }
+
+ if (strspn(buf, "fast") == 4 && strncmp(buf, "fast", 4) == 0)
+ {
+ int rc;
/*
* If trigger file found, we *must* delete it. Here's why: When
***************
*** 379,391 ****
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
return true;
}
!
return false;
}
--- 444,464 ----
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s",
! triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
+
+ Failover = FastFailover;
+ fprintf(stderr, "trigger file found: fast failover\n");
+ fflush(stderr);
return true;
}
!
! fprintf(stderr, "WARNING: invalid content in \"%s\"\n",
! triggerPath);
! fflush(stderr);
return false;
}
***************
*** 552,559 ****
break;
case 't': /* Trigger file */
triggerPath = optarg;
- if (CheckForExternalTrigger())
- exit(1); /* Normal exit, with non-zero */
break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
--- 625,630 ----
***************
*** 676,697 ****
/*
* Main wait loop
*/
! while (!CustomizableNextWALFileReady() && !triggered)
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! triggered = true;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit\n");
fflush(stderr);
}
}
else
{
if (debug)
{
--- 747,781 ----
/*
* Main wait loop
*/
! while (!CheckForExternalTrigger() && !CustomizableNextWALFileReady())
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! Failover = FastFailover;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit: fast failover\n");
fflush(stderr);
}
+ break;
}
else
{
+ waittime += sleeptime;
+ if (waittime >= maxwaittime && maxwaittime > 0)
+ {
+ Failover = FastFailover;
+ if (debug)
+ {
+ fprintf(stderr, "\nTimed out after %d seconds: fast failover\n",
+ waittime);
+ fflush(stderr);
+ }
+ break;
+ }
if (debug)
{
***************
*** 700,722 ****
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
-
- waittime += sleeptime;
-
- if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0)))
- {
- triggered = true;
- if (debug && waittime >= maxwaittime && maxwaittime > 0)
- fprintf(stderr, "\nTimed out after %d seconds\n", waittime);
- }
}
}
/*
* Action on exit
*/
! if (triggered)
! exit(1); /* Normal exit, with non-zero */
/*
* Once we have restored this file successfully we can remove some prior
--- 784,797 ----
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
}
}
/*
* Action on exit
*/
! if (Failover == FastFailover)
! exit(1);
/*
* Once we have restored this file successfully we can remove some prior
Index: doc/src/sgml/pgstandby.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/pgstandby.sgml,v
retrieving revision 2.7
diff -c -r2.7 pgstandby.sgml
*** doc/src/sgml/pgstandby.sgml 27 Feb 2009 09:30:21 -0000 2.7
--- doc/src/sgml/pgstandby.sgml 8 Apr 2009 01:07:36 -0000
***************
*** 92,97 ****
--- 92,138 ----
is specified,
the <replaceable>archivelocation</> directory must be writable too.
</para>
+ <para>
+ There are two ways to fail over a <quote>warm standby</> database server.
+ You control the type of failover by creating different trigger files
+ (if <literal>-t</> has been specified).
+
+ <variablelist>
+ <varlistentry>
+ <term>Smart Failover</term>
+ <listitem>
+ <para>
+ If the trigger file containing <literal>smart</> exists,
+ <application>pg_standby</application> acts as
+ <literal>cp</> or <literal>ln</> command itself,
+ and recovery waits to finish until all the available
+ WAL files are applied. It's guaranteed that no
+ <emphasis>available</> transactions are lost.
+ But it might take some times before finishing failover.
+ </para>
+ <para>
+ An empty trigger file also smartly performs failover.
+ </para>
+ <para>
+ Note that the trigger file remains even after failover.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Fast Failover</term>
+ <listitem>
+ <para>
+ The existence of the trigger file containing <literal>fast</>
+ causes recovery to end immediately even if the available
+ WAL files remain. So, some transactions might be lost.
+ </para>
+ <para>
+ Note that the trigger file is removed at failover.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
<table>
<title><application>pg_standby</> options</title>
***************
*** 177,184 ****
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should cause recovery to end
! whether or not the next WAL file is available.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
--- 218,224 ----
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should perform failover.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
***************
*** 190,196 ****
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up.
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
--- 230,237 ----
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up
! (Fast Failover).
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
***************
*** 236,242 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 277,284 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
***************
*** 277,283 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 319,326 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
Fujii Masao wrote:
Hi,
On Wed, Apr 8, 2009 at 6:56 AM, Guillaume Smet <guillaume.smet@gmail.com> wrote:
On Fri, Apr 3, 2009 at 5:42 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
Here is the patch;
- Smart failover is chosen if the trigger file labeled "smart" or
an empty one exists.
- Fast failover is chosen if the trigger file labeled "fast" exists,
the signal (SIGUSR1 or SIGINT) is received or the wait timeout
happens.After some further thoughts, +1 for this approach too.
I think you imply 'containing "smart"' not 'labeled "smart"'.
"Labeled" is confusing IMHO.Thanks for the comment!
I corrected such confusing expression.
+ if (strspn(buf, "smart") == 5 && strncmp(buf, "smart", 5) == 0) + {
The strspn() call seems pointless here.
One problem with this patch is that in smart mode, the trigger file is
not deleted. That's different from current pg_standby behavior, and
makes accidental failovers after one failover more likely.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Hi,
On Thu, Apr 9, 2009 at 9:47 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
+ if (strspn(buf, "smart") == 5 && strncmp(buf, "smart", 5) == 0) + {The strspn() call seems pointless here.
OK, I'll get rid of it.
One problem with this patch is that in smart mode, the trigger file is not
deleted. That's different from current pg_standby behavior, and makes
accidental failovers after one failover more likely.
Yes, it's because pg_standby cannot be sure when the trigger file
can be removed in smart mode. If the trigger file is deleted as soon
as it's found, just like in fast mode, pg_standby may keep waiting
for WAL file again.
One idea to solve this problem is to tell pg_standby as a
command-line argument about whether the trigger file can be
removed. That parameter value can be set to 'true' when the last
applied record is re-fetched. Though pg_standby is called to
restore timeline history files also after that point, the trigger file
is already unnecessary (pg_standby doesn't wait for history file).
Specifically, if restore_command contains new % option (%e?),
it's replaced by the boolean value which indicates whether the
trigger file can be deleted. This value is set to 'true' when the
startup process re-fetches the last valid record, 'false' otherwise.
In smart mode, pg_standby determines whether to delete the
trigger file according to that value.
Comments?
Or, do you have any better idea?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Fri, Apr 10, 2009 at 5:47 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
One idea to solve this problem is to tell pg_standby as a
command-line argument about whether the trigger file can be
removed. That parameter value can be set to 'true' when the last
applied record is re-fetched. Though pg_standby is called to
restore timeline history files also after that point, the trigger file
is already unnecessary (pg_standby doesn't wait for history file).Specifically, if restore_command contains new % option (%e?),
it's replaced by the boolean value which indicates whether the
trigger file can be deleted. This value is set to 'true' when the
startup process re-fetches the last valid record, 'false' otherwise.
In smart mode, pg_standby determines whether to delete the
trigger file according to that value.Comments?
Hmmm, it seems overly complicated but I don't know the code of pg_standby.
Or, do you have any better idea?
Wouldn't it be possible to have a global switch (let's name it
startCluster, default to false) which is set to true when the trigger
file is found for the first time? You would then be able to remove the
trigger file and let the cluster start by checking this variable.
One more time, I don't know the code of pg_standby so it may be a stupid idea.
--
Guillaume
Fujii-san,
I like the new patch using the content of the file to determine the
mode. Much easier to use at failover time.
On Fri, 2009-04-10 at 12:47 +0900, Fujii Masao wrote:
One problem with this patch is that in smart mode, the trigger file is not
deleted. That's different from current pg_standby behavior, and makes
accidental failovers after one failover more likely.Yes, it's because pg_standby cannot be sure when the trigger file
can be removed in smart mode. If the trigger file is deleted as soon
as it's found, just like in fast mode, pg_standby may keep waiting
for WAL file again.
My understanding of smart mode is fairly simple:
if (triggered)
{
if (smartMode && nextWALfile+1 exists)
exit(0);
else
{
delete trigger file
exit(1);
}
}
If you perform a file lookahead (the +1) as shown above then you avoid
the problem Heikki observes.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
On Fri, Apr 10, 2009 at 6:31 PM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:
On Fri, Apr 10, 2009 at 5:47 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
One idea to solve this problem is to tell pg_standby as a
command-line argument about whether the trigger file can be
removed. That parameter value can be set to 'true' when the last
applied record is re-fetched. Though pg_standby is called to
restore timeline history files also after that point, the trigger file
is already unnecessary (pg_standby doesn't wait for history file).Specifically, if restore_command contains new % option (%e?),
it's replaced by the boolean value which indicates whether the
trigger file can be deleted. This value is set to 'true' when the
startup process re-fetches the last valid record, 'false' otherwise.
In smart mode, pg_standby determines whether to delete the
trigger file according to that value.Comments?
Hmmm, it seems overly complicated but I don't know the code of pg_standby.
Yes, I'd also like to simplify it more.
Or, do you have any better idea?
Wouldn't it be possible to have a global switch (let's name it
startCluster, default to false) which is set to true when the trigger
file is found for the first time? You would then be able to remove the
trigger file and let the cluster start by checking this variable.One more time, I don't know the code of pg_standby so it may be a stupid idea.
Thanks for the suggestion!
Since pg_standby is executed for each file, such variable cannot
be taken over to the next execution, i.e. it's reset each time. So,
the current patch have left the trigger file until the end.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Hi,
On Sat, Apr 11, 2009 at 1:31 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
Fujii-san,
I like the new patch using the content of the file to determine the
mode. Much easier to use at failover time.On Fri, 2009-04-10 at 12:47 +0900, Fujii Masao wrote:
One problem with this patch is that in smart mode, the trigger file is not
deleted. That's different from current pg_standby behavior, and makes
accidental failovers after one failover more likely.Yes, it's because pg_standby cannot be sure when the trigger file
can be removed in smart mode. If the trigger file is deleted as soon
as it's found, just like in fast mode, pg_standby may keep waiting
for WAL file again.My understanding of smart mode is fairly simple:
if (triggered)
{
if (smartMode && nextWALfile+1 exists)
exit(0);
else
{
delete trigger file
exit(1);
}
}If you perform a file lookahead (the +1) as shown above then you avoid
the problem Heikki observes.
Thanks for the suggestion!
A lookahead (the +1) may have pg_standby get stuck as follows.
Am I missing something?
1. the trigger file containing "smart" is created.
2. pg_standby is executed.
2-1. nextWALfile is restored.
2-2. the trigger file is deleted because nextWALfile+1 doesn't exist.
3. the restored nextWALfile is applied.
4. pg_standby is executed again to restore nextWALfile+1.
5. pg_standby gets stuck because the trigger file and nextWALfile+1
don't exist.
But, a lookahead nextWALfile seems to work fine.
if (triggered)
{
if (smartMode && nextWALfile exists)
exit(0)
else
{
delete trigger file
exit(1)
}
}
1. the trigger file containing "smart" is created.
2. pg_standby is executed.
2-1. nextWALfile is restored.
3. the restored nextWALfile is applied.
4. pg_standby is executed again to restore nextWALfile+1.
4-1. the trigger file is deleted because nextWALfile+1 doesn't exist.
5. the startup process fails to read nextWALfile+1.
6. pg_standby is executed again to re-fetch nextWALfile.
6-1. nextWALfile is restored.
6-2. pg_standby doesn't get stuck because nextWALfile exists.
Furthermore, pg_standby may have to check if nextWALfile exists
not only in archiveLocation but also in pg_xlog. Because, when
pg_xlog of the primary server can be read at failover, WAL files
in it may be copied to pg_xlog of the standby server to be applied.
(but, not sure if it's better to copy such files to pg_xlog instead of
archiveLocation in this case).
Comments?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Mon, Apr 13, 2009 at 7:52 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
1. the trigger file containing "smart" is created.
2. pg_standby is executed.
2-1. nextWALfile is restored.
2-2. the trigger file is deleted because nextWALfile+1 doesn't exist.
3. the restored nextWALfile is applied.
4. pg_standby is executed again to restore nextWALfile+1.
I don't think it should happen. IMHO, it's an acceptable compromise to
replay all the WAL files present when I created the trigger file. So
if I have the smart shutdown trigger file and I don't have any
nextWALfile+1, I can remove the trigger file and stop the recovery:
pg_standby won't be executed again after that, even if a nextWALfile+1
appeared while replaying the previous WAL file.
That said, stupid question: do we have a way to know the nextWALfile+1
name to test if it exists? nextWALfile is transmitted through the
restore_command API and I'm wondering if we can have nextWALfile+1
name without changing the restore_command API.
--
Guillaume
Hi,
On Mon, Apr 13, 2009 at 7:21 PM, Guillaume Smet
<guillaume.smet@gmail.com> wrote:
On Mon, Apr 13, 2009 at 7:52 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
1. the trigger file containing "smart" is created.
2. pg_standby is executed.
2-1. nextWALfile is restored.
2-2. the trigger file is deleted because nextWALfile+1 doesn't exist.
3. the restored nextWALfile is applied.
4. pg_standby is executed again to restore nextWALfile+1.I don't think it should happen. IMHO, it's an acceptable compromise to
replay all the WAL files present when I created the trigger file. So
if I have the smart shutdown trigger file and I don't have any
nextWALfile+1, I can remove the trigger file and stop the recovery:
pg_standby won't be executed again after that, even if a nextWALfile+1
appeared while replaying the previous WAL file.
The scenario which I described is not related to whether the
nextWALfile+1 exists or not. To clarify the detail of it;
If pg_standby restores nextWALfile, deletes the trigger file and
exits with 1 (i.e. tell the end of recovery to the startup process),
the startup process considers that pg_standby failed,
and tries to read the nextWALfile in pg_xlog instead of the
restored file named "RECOVERYXLOG". This is undesirable
behavior because some transactions would be lost if nextWALfile
in pg_xlog doesn't exist. So, exit(0) should be called when
nextWALfile exists.
On the other hand, if pg_standby restores the nextWALfile,
deletes the trigger file and calls exit(0), the startup process
replays the restored file and tries to read the nextWALfile+1
because it doesn't know if the nextWALfile is the last valid WAL
file. So, pg_standby may be executed again even after the trigger
file is deleted.
Am I missing something?
That said, stupid question: do we have a way to know the nextWALfile+1
name to test if it exists? nextWALfile is transmitted through the
restore_command API and I'm wondering if we can have nextWALfile+1
name without changing the restore_command API.
Probably Yes; the following three steps are required, I think.
- Get the timeline, logid and segid from the name of the nextWALfile.
- Increment the logid and segid pair using NextLogSeg macro.
- Calculate the name of the nextWALfile+1 using XLogFileName macro.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Mon, 2009-04-13 at 14:52 +0900, Fujii Masao wrote:
if (triggered)
{
if (smartMode && nextWALfile exists)
exit(0)
else
{
delete trigger file
exit(1)
}
}
This looks to be the correct one.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
On Mon, Apr 13, 2009 at 2:52 PM, Fujii Masao <masao.fujii@gmail.com> wrote:
But, a lookahead nextWALfile seems to work fine.
if (triggered)
{
if (smartMode && nextWALfile exists)
exit(0)
else
{
delete trigger file
exit(1)
}
}
Umm... in this algorithm, the trigger file remains after failover
if the nextWALfile has the invalid record which means the end
of WAL files.
I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.
A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.
How does that sound?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Mon, 2009-04-13 at 14:52 +0900, Fujii Masao wrote:
A lookahead (the +1) may have pg_standby get stuck as follows.
Am I missing something?1. the trigger file containing "smart" is created.
2. pg_standby is executed.
2-1. nextWALfile is restored.
2-2. the trigger file is deleted because nextWALfile+1 doesn't exist.
3. the restored nextWALfile is applied.
4. pg_standby is executed again to restore nextWALfile+1.
This can't happen. (4) will never occur when (2-2) has occurred. A
non-zero error code means file not available which will cause recovery
to end and hence no requests for further WAL files are made.
It does *seem* as if there is a race condition there in that another WAL
file may arrive after we have taken the decision there are no more WAL
files, but it's not a problem. That could happen if we issue the trigger
while the master is still up, which is a mistake - why would we do that?
If we only issue the trigger once we are happy the master is down then
we don't get a problem.
So lets do it the next+1 way, when triggered.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
On Tue, Apr 14, 2009 at 6:35 PM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Mon, 2009-04-13 at 14:52 +0900, Fujii Masao wrote:
A lookahead (the +1) may have pg_standby get stuck as follows.
Am I missing something?1. the trigger file containing "smart" is created.
2. pg_standby is executed.
2-1. nextWALfile is restored.
2-2. the trigger file is deleted because nextWALfile+1 doesn't exist.
3. the restored nextWALfile is applied.
4. pg_standby is executed again to restore nextWALfile+1.This can't happen. (4) will never occur when (2-2) has occurred. A
non-zero error code means file not available which will cause recovery
to end and hence no requests for further WAL files are made.
When pg_standby exits with non-zero code, (3) and (4) will never
occur, and the transactions in nextWALfile will be lost. So, in (2-2),
pg_standby has to call exit(0), I think.
On the other hand, if exit(0) is called in (2-2), the above scenario
happens.
It does *seem* as if there is a race condition there in that another WAL
file may arrive after we have taken the decision there are no more WAL
files, but it's not a problem. That could happen if we issue the trigger
while the master is still up, which is a mistake - why would we do that?
If we only issue the trigger once we are happy the master is down then
we don't get a problem.
Yeah, I agree that such race condition is not a problem. The
trigger file has to be created after all the WAL files arrive at
the standby server.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
I've been following the thread with growing lack of understanding why
this is so hardly discussed, and I went back to the documentation of
what the restore_command should do (
http://www.postgresql.org/docs/8.3/static/warm-standby.html )
While the algorithm presented in the pseudocode isn't dealing too good
with a situation where the trigger is set while the restore_command is
sleeping (this should be handled better in a real implementation), the
code says
"Restore all wal files. If no more wal files are present, stop restoring
if the trigger is set; otherwise wait for a new wal file".
Since pg_standby is meant as implementation of restore_command, it has
to follow the directive stated above; *anything else is a bug*.
pg_standby currently does *not* obey this directive, and has that
documented, but a documented bug still is a bug.
Conclusion: There's no "new trigger option" needed, instead pg_standby
has to be fixed so it does what the warm standby option of postgres
needs. The trigger is only to be examined if no more files are
restorable, and only once.
Regards,
Andreas
Hi,
On Wed, Apr 15, 2009 at 3:30 AM, Andreas Pflug
<pgadmin@pse-consulting.de> wrote:
I've been following the thread with growing lack of understanding why
this is so hardly discussed, and I went back to the documentation of
what the restore_command should do (
http://www.postgresql.org/docs/8.3/static/warm-standby.html )While the algorithm presented in the pseudocode isn't dealing too good
with a situation where the trigger is set while the restore_command is
sleeping (this should be handled better in a real implementation), the
code says"Restore all wal files. If no more wal files are present, stop restoring
if the trigger is set; otherwise wait for a new wal file".Since pg_standby is meant as implementation of restore_command, it has
to follow the directive stated above; *anything else is a bug*.
pg_standby currently does *not* obey this directive, and has that
documented, but a documented bug still is a bug.Conclusion: There's no "new trigger option" needed, instead pg_standby
has to be fixed so it does what the warm standby option of postgres
needs. The trigger is only to be examined if no more files are
restorable, and only once.
Yeah, as a result of the discussion on that thread, I'll change
the default behavior instead of adding new trigger option.
But, I'm not going to get rid of the current behavior; it's chosen
if the trigger file containing "fast" exists. On the other hand,
new behavior is chosen when the trigger file containing "smart"
or an empty one exists (default).
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Hi,
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com> wrote:
I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.
Here is the revised patch as above.
If you notice something, please feel free to comment.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
pgstandby_change_trigger_0415.patchapplication/octet-stream; name=pgstandby_change_trigger_0415.patchDownload
Index: contrib/pg_standby/pg_standby.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pg_standby/pg_standby.c,v
retrieving revision 1.21
diff -c -r1.21 pg_standby.c
*** contrib/pg_standby/pg_standby.c 26 Mar 2009 22:29:13 -0000 1.21
--- contrib/pg_standby/pg_standby.c 15 Apr 2009 07:51:56 -0000
***************
*** 52,58 ****
int keepfiles = 0; /* number of WAL files to keep, 0 keep all */
int maxretries = 3; /* number of retries on restore command */
bool debug = false; /* are we debugging? */
- bool triggered = false; /* have we been triggered? */
bool need_cleanup = false; /* do we need to remove files from
* archive? */
--- 52,57 ----
***************
*** 69,74 ****
--- 68,99 ----
char exclusiveCleanupFileName[MAXPGPATH]; /* the file we need to
* get from archive */
+ /*
+ * Two types of failover are supported (smart and fast failover).
+ *
+ * The content of the trigger file determines the type of failover.
+ * If the trigger file containing "smart" exists, smart failover is chosen;
+ * pg_standby acts as cp or ln command itself, and recovery waits to
+ * finish until all the available WAL files are applied. It's
+ * guaranteed that no "available" commit transactions are lost. But
+ * it might take some times before finishing recovery.
+ *
+ * On the other hand, the existence of the trigger file with "fast"
+ * causes recovery to end immediately even if the available WAL files
+ * remain. So, some transactions might be lost.
+ *
+ * An empty trigger file performs smart failover.
+ *
+ * Fast failover is triggered by the signal (SIGUSR1 or SIGINT).
+ *
+ * A timeout causes smart failover.
+ */
+ #define NoFailover 0
+ #define SmartFailover 1
+ #define FastFailover 2
+
+ static int Failover = NoFailover;
+
#define RESTORE_COMMAND_COPY 0
#define RESTORE_COMMAND_LINK 1
int restoreCommandType;
***************
*** 108,114 ****
*
* As an example, and probably the common case, we use either
* cp/ln commands on *nix, or copy/move command on Windows.
- *
*/
static void
CustomizableInitialize(void)
--- 133,138 ----
***************
*** 357,363 ****
static bool
CheckForExternalTrigger(void)
{
! int rc;
/*
* Look for a trigger file, if that option has been selected
--- 381,388 ----
static bool
CheckForExternalTrigger(void)
{
! char buf[32];
! FILE *fd;
/*
* Look for a trigger file, if that option has been selected
***************
*** 365,374 ****
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (triggerPath && stat(triggerPath, &stat_buf) == 0)
{
! fprintf(stderr, "trigger file found\n");
fflush(stderr);
/*
* If trigger file found, we *must* delete it. Here's why: When
--- 390,439 ----
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (!triggerPath || stat(triggerPath, &stat_buf) != 0)
! return false;
!
! /*
! * An empty trigger file performs smart failover
! */
! if (stat_buf.st_size == 0)
{
! Failover = SmartFailover;
! fprintf(stderr, "trigger file found: smart failover\n");
fflush(stderr);
+ return true;
+ }
+
+ if ((fd = fopen(triggerPath, "r")) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not open \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ return false;
+ }
+
+ if (fgets(buf, sizeof(buf), fd) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not read \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ fclose(fd);
+ return false;
+ }
+
+ fclose(fd);
+
+ if (strncmp(buf, "smart", 5) == 0)
+ {
+ Failover = SmartFailover;
+ fprintf(stderr, "trigger file found: smart failover\n");
+ fflush(stderr);
+ return true;
+ }
+
+ if (strncmp(buf, "fast", 4) == 0)
+ {
+ int rc;
/*
* If trigger file found, we *must* delete it. Here's why: When
***************
*** 379,391 ****
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
return true;
}
!
return false;
}
--- 444,464 ----
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s",
! triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
+
+ Failover = FastFailover;
+ fprintf(stderr, "trigger file found: fast failover\n");
+ fflush(stderr);
return true;
}
!
! fprintf(stderr, "WARNING: invalid content in \"%s\"\n",
! triggerPath);
! fflush(stderr);
return false;
}
***************
*** 552,559 ****
break;
case 't': /* Trigger file */
triggerPath = optarg;
- if (CheckForExternalTrigger())
- exit(1); /* Normal exit, with non-zero */
break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
--- 625,630 ----
***************
*** 659,664 ****
--- 730,755 ----
strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".history"),
".history") == 0)
{
+ /*
+ * Delete the trigger file whenever a history file is requested
+ * (at the beginning and end of recovery). Which prevents the
+ * accidentally remaining trigger file from harming the warm-standby,
+ * and an unnecessary trigger file from remaining after recovery.
+ */
+ if (triggerPath && stat(triggerPath, &stat_buf) == 0)
+ {
+ int rc;
+
+ rc = unlink(triggerPath);
+ if (rc != 0)
+ {
+ fprintf(stderr, "\n ERROR: could not remove \"%s\": %s",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ exit(rc);
+ }
+ }
+
nextWALFileType = XLOG_HISTORY;
if (RestoreWALFileForRecovery())
exit(0);
***************
*** 676,697 ****
/*
* Main wait loop
*/
! while (!CustomizableNextWALFileReady() && !triggered)
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! triggered = true;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit\n");
fflush(stderr);
}
}
else
{
if (debug)
{
--- 767,801 ----
/*
* Main wait loop
*/
! while (!CheckForExternalTrigger() && !CustomizableNextWALFileReady())
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! Failover = FastFailover;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit: fast failover\n");
fflush(stderr);
}
+ break;
}
else
{
+ waittime += sleeptime;
+ if (waittime >= maxwaittime && maxwaittime > 0)
+ {
+ Failover = FastFailover;
+ if (debug)
+ {
+ fprintf(stderr, "\nTimed out after %d seconds: fast failover\n",
+ waittime);
+ fflush(stderr);
+ }
+ break;
+ }
if (debug)
{
***************
*** 700,722 ****
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
-
- waittime += sleeptime;
-
- if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0)))
- {
- triggered = true;
- if (debug && waittime >= maxwaittime && maxwaittime > 0)
- fprintf(stderr, "\nTimed out after %d seconds\n", waittime);
- }
}
}
/*
* Action on exit
*/
! if (triggered)
! exit(1); /* Normal exit, with non-zero */
/*
* Once we have restored this file successfully we can remove some prior
--- 804,817 ----
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
}
}
/*
* Action on exit
*/
! if (Failover == FastFailover)
! exit(1);
/*
* Once we have restored this file successfully we can remove some prior
Index: doc/src/sgml/pgstandby.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/pgstandby.sgml,v
retrieving revision 2.7
diff -c -r2.7 pgstandby.sgml
*** doc/src/sgml/pgstandby.sgml 27 Feb 2009 09:30:21 -0000 2.7
--- doc/src/sgml/pgstandby.sgml 15 Apr 2009 07:51:56 -0000
***************
*** 92,97 ****
--- 92,132 ----
is specified,
the <replaceable>archivelocation</> directory must be writable too.
</para>
+ <para>
+ There are two ways to fail over a <quote>warm standby</> database server.
+ You control the type of failover by creating different trigger files
+ (if <literal>-t</> has been specified).
+
+ <variablelist>
+ <varlistentry>
+ <term>Smart Failover</term>
+ <listitem>
+ <para>
+ If the trigger file containing <literal>smart</> exists,
+ <application>pg_standby</application> acts as
+ <literal>cp</> or <literal>ln</> command itself,
+ and recovery waits to finish until all the available
+ WAL files are applied. It's guaranteed that no
+ <emphasis>available</> transactions are lost.
+ But it might take some times before finishing failover.
+ </para>
+ <para>
+ An empty trigger file also smartly performs failover.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Fast Failover</term>
+ <listitem>
+ <para>
+ The existence of the trigger file containing <literal>fast</>
+ causes recovery to end immediately even if the available
+ WAL files remain. So, some transactions might be lost.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
<table>
<title><application>pg_standby</> options</title>
***************
*** 177,188 ****
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should cause recovery to end
! whether or not the next WAL file is available.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
<filename>/tmp/pgsql.trigger.5432</>.
</entry>
</row>
<row>
--- 212,224 ----
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should perform failover.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
<filename>/tmp/pgsql.trigger.5432</>.
+ Note that a trigger file is deleted at the end of recovery
+ regardless of failover type.
</entry>
</row>
<row>
***************
*** 190,196 ****
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up.
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
--- 226,233 ----
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up
! (Fast Failover).
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
***************
*** 236,242 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 273,280 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
***************
*** 277,283 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 315,322 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
On Wed, 2009-04-15 at 17:02 +0900, Fujii Masao wrote:
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com> wrote:
I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
If you notice something, please feel free to comment.
Deleting the trigger file when we request a history file works in most
cases, but not in all. We also request a history file when we switch
timelines, so code comments need slight modification.
If take a base backup, switchover and then try to regen the primary from
the base backup we would need to switch timelines, which could be
problematic. That is unlikely, so we should at least very clearly
document the actual behaviour, as we do in the code comments.
I think your wording that smart mode guarantees no data will be lost is
a little strong. I'd say "on successful completion all WAL records will
be replayed resulting in zero data loss".
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi Simon,
Thanks for the comments!
On Thu, Apr 16, 2009 at 2:56 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Wed, 2009-04-15 at 17:02 +0900, Fujii Masao wrote:
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com> wrote:
I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
If you notice something, please feel free to comment.
Deleting the trigger file when we request a history file works in most
cases, but not in all. We also request a history file when we switch
timelines, so code comments need slight modification.If take a base backup, switchover and then try to regen the primary from
the base backup we would need to switch timelines, which could be
problematic. That is unlikely, so we should at least very clearly
document the actual behaviour, as we do in the code comments.
"switch timelines" means that a new timeline ID is assigned at
the end of archive recovery? If so, even in this case, there is
no problem with deleting the trigger file, I think.
Or, am I misunderstanding?
I think your wording that smart mode guarantees no data will be lost is
a little strong. I'd say "on successful completion all WAL records will
be replayed resulting in zero data loss".
Sounds good. I'll change the wording.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Andreas Pflug wrote:
I've been following the thread with growing lack of understanding why
this is so hardly discussed, and I went back to the documentation of
what the restore_command should do (
http://www.postgresql.org/docs/8.3/static/warm-standby.html )While the algorithm presented in the pseudocode isn't dealing too good
with a situation where the trigger is set while the restore_command is
sleeping (this should be handled better in a real implementation), the
code says"Restore all wal files. If no more wal files are present, stop restoring
if the trigger is set; otherwise wait for a new wal file".Since pg_standby is meant as implementation of restore_command, it has
to follow the directive stated above; *anything else is a bug*.
pg_standby currently does *not* obey this directive, and has that
documented, but a documented bug still is a bug.
I think you're interpreting the chapter too strongly. The provided
pseudo-code is just an example of a suitable restore_command, it doesn't
say that pg_standby behaves exactly like that.
I agree we should change the default behavior, though.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Fujii Masao wrote:
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com> wrote:
I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
I think we have gone off to an overly complicated solution. pg_standby
shouldn't need to special-case history files, or know what order the
server will ask for them.
What's wrong with just this: (ignoring the missing fast option)
--- a/contrib/pg_standby/pg_standby.c
+++ b/contrib/pg_standby/pg_standby.c
@@ -552,8 +552,6 @@ main(int argc, char **argv)
break;
case 't': /* Trigger file */
triggerPath = optarg;
- if (CheckForExternalTrigger())
- exit(1); /* Normal exit, with non-zero */
break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
ie. only check and delete the trigger file once the server requests a
file that doesn't exist.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Hi,
On Mon, Apr 20, 2009 at 6:06 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
Fujii Masao wrote:
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com>
wrote:I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
I think we have gone off to an overly complicated solution. pg_standby
shouldn't need to special-case history files, or know what order the server
will ask for them.What's wrong with just this: (ignoring the missing fast option)
--- a/contrib/pg_standby/pg_standby.c +++ b/contrib/pg_standby/pg_standby.c @@ -552,8 +552,6 @@ main(int argc, char **argv) break; case 't': /* Trigger file */ triggerPath = optarg; - if (CheckForExternalTrigger()) - exit(1); /* Normal exit, with non-zero */ break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg);ie. only check and delete the trigger file once the server requests a file
that doesn't exist.
Thanks for the suggestion! I have three comments.
1)
Though some users want to save the fast failover mode, this
solution doesn't provide it. You think that that mode is
unnecessary?
2)
This solution would go wrong when there are the WAL files in
pg_xlog; If pg_xlog of the primary server can be read at failover
(it's not node failure but process failure case), the WAL files in
it may be copied to pg_xlog of the standby server in order to
prevent the transaction loss.
On the other hand, we can copy such files to the archival storage
instead of pg_xlog. In this case, your simple solution goes well,
but recovery would unexpectedly end without the trigger file
because those WAL files have the invalid record which means
the end of WAL.
I'd like to control when the standby will come up as a normal
server. So, I think that pg_standby should cope with also the
above situation which I described. What is your opinion?
3)
This solution has a race condition; some transactions would
be lost if WAL file is shipped from the primary server and the
trigger file is created, while pg_standby is sleeping, because
pg_standby checks previously whether a trigger file exists.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Heikki Linnakangas wrote:
Andreas Pflug wrote:
I've been following the thread with growing lack of understanding why
this is so hardly discussed, and I went back to the documentation of
what the restore_command should do (
http://www.postgresql.org/docs/8.3/static/warm-standby.html )While the algorithm presented in the pseudocode isn't dealing too good
with a situation where the trigger is set while the restore_command is
sleeping (this should be handled better in a real implementation), the
code says"Restore all wal files. If no more wal files are present, stop restoring
if the trigger is set; otherwise wait for a new wal file".Since pg_standby is meant as implementation of restore_command, it has
to follow the directive stated above; *anything else is a bug*.
pg_standby currently does *not* obey this directive, and has that
documented, but a documented bug still is a bug.I think you're interpreting the chapter too strongly. The provided
pseudo-code is just an example of a suitable restore_command, it
doesn't say that pg_standby behaves exactly like that.
After reading that chapter, I assumed that pg_standby actually does work
like this, and skipped reading the pg_standby specific doc....
The pgsql doc tries hard to give best advice for common situations,
especially for integrity and safety issues. IMHO it's best to have the
warm-standby chapter as reference how things should work for typical
use-cases.
Regards,
Andreas
Fujii Masao wrote:
On Mon, Apr 20, 2009 at 6:06 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:What's wrong with just this: (ignoring the missing fast option)
--- a/contrib/pg_standby/pg_standby.c +++ b/contrib/pg_standby/pg_standby.c @@ -552,8 +552,6 @@ main(int argc, char **argv) break; case 't': /* Trigger file */ triggerPath = optarg; - if (CheckForExternalTrigger()) - exit(1); /* Normal exit, with non-zero */ break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg);ie. only check and delete the trigger file once the server requests a file
that doesn't exist.Thanks for the suggestion! I have three comments.
1)
Though some users want to save the fast failover mode, this
solution doesn't provide it. You think that that mode is
unnecessary?
No I just left it out to keep it simple. The patch was only meant to
make the point, I didn't intend it to be applied as is.
2)
This solution would go wrong when there are the WAL files in
pg_xlog; If pg_xlog of the primary server can be read at failover
(it's not node failure but process failure case), the WAL files in
it may be copied to pg_xlog of the standby server in order to
prevent the transaction loss.On the other hand, we can copy such files to the archival storage
instead of pg_xlog. In this case, your simple solution goes well,
but recovery would unexpectedly end without the trigger file
because those WAL files have the invalid record which means
the end of WAL.I'd like to control when the standby will come up as a normal
server. So, I think that pg_standby should cope with also the
above situation which I described. What is your opinion?
Hmm, the user manual instructs to copy any unarchived files directly
into pg_xlog, but I guess you could copy them to the archive instead.
At the end of archive recovery, the server always probes for the
timeline by requesting history files until it fails to find one. That
probing should remove the trigger file if it hasn't been removed by
then. It's a bit coincidental to rely on that, but at least it's simple.
The assumption we're making is that the server won't exit recovery
before asking restore_command for a file that doesn't exist.
3)
This solution has a race condition; some transactions would
be lost if WAL file is shipped from the primary server and the
trigger file is created, while pg_standby is sleeping, because
pg_standby checks previously whether a trigger file exists.
Yeah, it should sleep only after checking for the trigger file. That
doesn't completely eliminate the race condition though: I think it
should check again that the WAL file doesn't exist after reading the
trigger file but before deleting it.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Mon, 2009-04-20 at 17:47 +0300, Heikki Linnakangas wrote:
At the end of archive recovery, the server always probes for the
timeline by requesting history files until it fails to find one. That
probing should remove the trigger file if it hasn't been removed by
then. It's a bit coincidental to rely on that, but at least it's simple.
The assumption we're making is that the server won't exit recovery
before asking restore_command for a file that doesn't exist.
If you really want to simplify this, then we should have a final_command
parameter for a command to be executed at the end of recovery. That
would make the change to pg_standby very simple and allow for a very
simple final_command also. That would make the logic similar to what we
do for aggregates: transition function and final function.
We could call it restore_cleanup_command or something similar.
I suspect this option will make you consider Fujii-san's patch in a
better light. :-)
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
On Mon, 2009-04-20 at 17:47 +0300, Heikki Linnakangas wrote:
At the end of archive recovery, the server always probes for the
timeline by requesting history files until it fails to find one. That
probing should remove the trigger file if it hasn't been removed by
then. It's a bit coincidental to rely on that, but at least it's simple.
The assumption we're making is that the server won't exit recovery
before asking restore_command for a file that doesn't exist.If you really want to simplify this, then we should have a final_command
parameter for a command to be executed at the end of recovery. That
would make the change to pg_standby very simple and allow for a very
simple final_command also. That would make the logic similar to what we
do for aggregates: transition function and final function.We could call it restore_cleanup_command or something similar.
Hmm, that might indeed be a cleaner interface. However, that throws the
idea of backpatching out of the window, and will make it impossible to
run a PG 8.4 pg_standby against a PG 8.3 server. Do we want to add the
new parameter for 8.4 anyway?
I suspect this option will make you consider Fujii-san's patch in a
better light. :-)
No, removing trigger file as soon as a non-existant file is requested
still seems simpler than deleting it whenever a timeline history file is
requested.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, 2009-04-21 at 14:17 +0300, Heikki Linnakangas wrote:
Simon Riggs wrote:
On Mon, 2009-04-20 at 17:47 +0300, Heikki Linnakangas wrote:
At the end of archive recovery, the server always probes for the
timeline by requesting history files until it fails to find one. That
probing should remove the trigger file if it hasn't been removed by
then. It's a bit coincidental to rely on that, but at least it's simple.
The assumption we're making is that the server won't exit recovery
before asking restore_command for a file that doesn't exist.If you really want to simplify this, then we should have a final_command
parameter for a command to be executed at the end of recovery. That
would make the change to pg_standby very simple and allow for a very
simple final_command also. That would make the logic similar to what we
do for aggregates: transition function and final function.We could call it restore_cleanup_command or something similar.
Hmm, that might indeed be a cleaner interface. However, that throws the
idea of backpatching out of the window, and will make it impossible to
run a PG 8.4 pg_standby against a PG 8.3 server. Do we want to add the
new parameter for 8.4 anyway?
Perhaps, let's see how we resolve the perceived 8.2 and 8.3 issues.
I suspect this option will make you consider Fujii-san's patch in a
better light. :-)No, removing trigger file as soon as a non-existant file is requested
still seems simpler than deleting it whenever a timeline history file is
requested.
If you do this, then you would have to change the procedure written into
the 8.3 docs also. Docs aren't backpatchable.
What you propose is *better* than raw pg_standby is now, but still not
enough in all cases, as I think you know. Simple isn't the requirement
here, is it?
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
If you do this, then you would have to change the procedure written into
the 8.3 docs also. Docs aren't backpatchable.What you propose is *better* than raw pg_standby is now, but still not
enough in all cases, as I think you know.
No, I don't. What is the case where it doesn't work?
Simple isn't the requirement here, is it?
Simplicity is always a virtue, because it leads to maintainability.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, 2009-04-21 at 14:28 +0300, Heikki Linnakangas wrote:
Simple isn't the requirement here, is it?
Simplicity is always a virtue, because it leads to maintainability.
"Simple enough" is a virtue. Less than that is not...
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
On Tue, Apr 21, 2009 at 8:28 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
Simon Riggs wrote:
If you do this, then you would have to change the procedure written into
the 8.3 docs also. Docs aren't backpatchable.What you propose is *better* than raw pg_standby is now, but still not
enough in all cases, as I think you know.No, I don't. What is the case where it doesn't work?
It's the case which I described as the 2nd comment to your
proposal.
1. pg_standby tries to restore a non-existent file
1-1. remove the trigger file
1-2. pg_standby exits with non-zero code
2. the startup process tries to read it from pg_xlog
2-1. it is applied
3. the startup process tries to restore the next file using pg_standby
3-1. pg_standby gets *stuck* since the requested file and trigger file
don't exist.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Fujii Masao wrote:
On Tue, Apr 21, 2009 at 8:28 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:Simon Riggs wrote:
What you propose is *better* than raw pg_standby is now, but still not
enough in all cases, as I think you know.No, I don't. What is the case where it doesn't work?
It's the case which I described as the 2nd comment to your
proposal.1. pg_standby tries to restore a non-existent file
1-1. remove the trigger file
1-2. pg_standby exits with non-zero code
2. the startup process tries to read it from pg_xlog
2-1. it is applied
3. the startup process tries to restore the next file using pg_standby
3-1. pg_standby gets *stuck* since the requested file and trigger file
don't exist.
Ahh, ok, I didn't understand the issue correctly before.
But wait a minute, we already have exactly the same problem with the
current 8.2/8.3 pg_standby, don't we? [tests]. Yes, we do.
Simon's suggestion of a separate restore_completion_command is very
attractive as it would provide an explicit place to hook up the deletion
of the trigger file. It seems useful anyway, you might want to put a
command there to e.g update a log file or launch some custom daemon
software when the recovery ends. The question then is what to do with
8.2 and 8.3? Even if we decided to keep the behavior that the failover
is triggered immediately (fast mode), pg_standby getting stuck if you
copy any WAL files directly into pg_xlog seems like a bug that needs to
be fixed.
Fujii's idea of deleting the trigger file when history file is requested
is the only proposal this far that works and doesn't require changes to
people's config files, so I guess that's what we'll have to do at least
for back-branches.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, 2009-04-21 at 15:55 +0300, Heikki Linnakangas wrote:
Fujii Masao wrote:
On Tue, Apr 21, 2009 at 8:28 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:Simon Riggs wrote:
What you propose is *better* than raw pg_standby is now, but still not
enough in all cases, as I think you know.No, I don't. What is the case where it doesn't work?
It's the case which I described as the 2nd comment to your
proposal.1. pg_standby tries to restore a non-existent file
1-1. remove the trigger file
1-2. pg_standby exits with non-zero code
2. the startup process tries to read it from pg_xlog
2-1. it is applied
3. the startup process tries to restore the next file using pg_standby
3-1. pg_standby gets *stuck* since the requested file and trigger file
don't exist.Ahh, ok, I didn't understand the issue correctly before.
But wait a minute, we already have exactly the same problem with the
current 8.2/8.3 pg_standby, don't we? [tests]. Yes, we do.Simon's suggestion of a separate restore_completion_command is very
attractive as it would provide an explicit place to hook up the deletion
of the trigger file. It seems useful anyway, you might want to put a
command there to e.g update a log file or launch some custom daemon
software when the recovery ends. The question then is what to do with
8.2 and 8.3? Even if we decided to keep the behavior that the failover
is triggered immediately (fast mode), pg_standby getting stuck if you
copy any WAL files directly into pg_xlog seems like a bug that needs to
be fixed.Fujii's idea of deleting the trigger file when history file is requested
is the only proposal this far that works and doesn't require changes to
people's config files, so I guess that's what we'll have to do at least
for back-branches.
Agreed. Fujii-san's proposal is the only one that covers all the
important things. The assumptions need careful documentation, as you
say.
The idea of a restore_completion_command does still sound attractive,
easy to implement and non-intrusive enough to do so right now.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Fujii Masao wrote:
Hi,
On Tue, Apr 21, 2009 at 8:28 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:Simon Riggs wrote:
If you do this, then you would have to change the procedure written into
the 8.3 docs also. Docs aren't backpatchable.What you propose is *better* than raw pg_standby is now, but still not
enough in all cases, as I think you know.No, I don't. What is the case where it doesn't work?
It's the case which I described as the 2nd comment to your
proposal.1. pg_standby tries to restore a non-existent file
1-1. remove the trigger file
1-2. pg_standby exits with non-zero code
2. the startup process tries to read it from pg_xlog
2-1. it is applied
3. the startup process tries to restore the next file using pg_standby
I'm a little confused. After pg_standby returned non-zero as indication
for end-of-recovery, the startup process shouldn't request another file
from pg_standby, right? Which means 3. should never happen (unless the
startup process stalls and restarts, in which case I find it normal that
another trigger required).
Regards,
Andreas
Andreas Pflug wrote:
I'm a little confused. After pg_standby returned non-zero as indication
for end-of-recovery, the startup process shouldn't request another file
from pg_standby, right?
Non-zero return value from restore_command doesn't mean end-of-recovery,
it means file-not-found. The server will try to open the WAL file from
pg_xlog if it's not found in archive (= restore_command returned
non-zero). If it's found in pg_xlog, it will then ask for the next WAL
file from the archive again. And on top of that, the server will ask for
history files until it finds one that doesn't exist to figure out the
new timeline.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, Apr 21, 2009 at 12:25:50PM +0100, Simon Riggs wrote:
No, removing trigger file as soon as a non-existant file is
requested still seems simpler than deleting it whenever a timeline
history file is requested.If you do this, then you would have to change the procedure written
into the 8.3 docs also. Docs aren't backpatchable.
Are you sure? We've found errors in them before and fixed them.
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
Fujii Masao wrote:
Hi,
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com> wrote:
I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
If you notice something, please feel free to comment.
Ok, looking at this in more detail now. A couple of small things:
We mustn't remove the trigger file immediately even in fast mode. As
noted elsewhere in this thread, we have the same bug in fast mode where
pg_standby gets stuck if you copy WAL files directly into pg_xlog.
pg_standby should exit with code 0 only if the file is restore
successfully. As the patch stands it also returns 0 when smart failover
is done.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Hi,
Thanks for reviewing the patch!
On Wed, Apr 22, 2009 at 4:27 AM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
Fujii Masao wrote:
Hi,
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com>
wrote:I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
If you notice something, please feel free to comment.
Ok, looking at this in more detail now. A couple of small things:
We mustn't remove the trigger file immediately even in fast mode. As noted
elsewhere in this thread, we have the same bug in fast mode where pg_standby
gets stuck if you copy WAL files directly into pg_xlog.
Yes, there is the same problem also in fast mode. But, in fast
mode, the trigger file has to be deleted immediately if it's found.
Otherwise, recovery may fail as follows.
1. pg_standby finds the trigger file for fast mode, and returns
non-zero without deleting the trigger file.
2. the startup process tries to read the WAL file from pg_xlog,
but it's not found.
3. the startup process tries to restore the last applied WAL file
using pg_standby.
4. (Again) pg_standby finds the trigger file for fast mode, and
returns non-zero without deleting the trigger file.
5. the startup process tries to read the last applied WAL file,
but it's not found.
(though the last applied file was of course restored before,
the restored one cannot be read here)
6. recovery fails because the last applied WAL file cannot be
read.
On the other hand, if pg_standby returns 0 also in fast mode
when the requested file and trigger file exist, ISTM that there
is not much difference between fast and smart mode; also in
fast mode, all the available WAL files would be applied.
So, I left fast mode as it was (as much as possible) though it has
the above problem, which is for backward compatibility.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Tue, 2009-04-21 at 09:59 -0700, David Fetter wrote:
On Tue, Apr 21, 2009 at 12:25:50PM +0100, Simon Riggs wrote:
No, removing trigger file as soon as a non-existant file is
requested still seems simpler than deleting it whenever a timeline
history file is requested.If you do this, then you would have to change the procedure written
into the 8.3 docs also. Docs aren't backpatchable.Are you sure? We've found errors in them before and fixed them.
The proposed change is not a bug fix -- hmmm, or is it?
I think we have a way that does not require this...
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Fujii Masao wrote:
On Wed, Apr 22, 2009 at 4:27 AM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:Fujii Masao wrote:
On Tue, Apr 14, 2009 at 2:41 PM, Fujii Masao <masao.fujii@gmail.com>
wrote:I'd like to propose another simple idea; pg_standby deletes the
trigger file *whenever* the nextWALfile is a timeline history file.
A timeline history file is restored at the end of recovery, so it's
guaranteed that the trigger file is deleted whether nextWALfile
exists or not.A timeline history file is restored also at the beginning of
recovery, so the accidentally remaining trigger file is deleted
in early warm-standby as a side-effect of this idea.Here is the revised patch as above.
If you notice something, please feel free to comment.
Ok, looking at this in more detail now. A couple of small things:
We mustn't remove the trigger file immediately even in fast mode. As noted
elsewhere in this thread, we have the same bug in fast mode where pg_standby
gets stuck if you copy WAL files directly into pg_xlog.Yes, there is the same problem also in fast mode. But, in fast
mode, the trigger file has to be deleted immediately if it's found.
Otherwise, recovery may fail as follows.1. pg_standby finds the trigger file for fast mode, and returns
non-zero without deleting the trigger file.
2. the startup process tries to read the WAL file from pg_xlog,
but it's not found.
3. the startup process tries to restore the last applied WAL file
using pg_standby.
4. (Again) pg_standby finds the trigger file for fast mode, and
returns non-zero without deleting the trigger file.
5. the startup process tries to read the last applied WAL file,
but it's not found.
(though the last applied file was of course restored before,
the restored one cannot be read here)
6. recovery fails because the last applied WAL file cannot be
read.On the other hand, if pg_standby returns 0 also in fast mode
when the requested file and trigger file exist, ISTM that there
is not much difference between fast and smart mode; also in
fast mode, all the available WAL files would be applied.
Hmm, pg_standby could truncate the trigger file, so that it acts like a
smart trigger in the subsequent pg_standby invocations. Assuming the
postgres user has write access to it; it probably does because it can
delete it, but conceivably it has only read access on the file but write
access on the directory it's in.
This is getting complicated, though. I guess it would be enough to
document that you mustn't copy any extra files into pg_xlog if you use a
fast trigger.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Hi,
On Thu, Apr 23, 2009 at 4:49 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
This is getting complicated, though. I guess it would be enough to document
that you mustn't copy any extra files into pg_xlog if you use a fast
trigger.
Agreed. I added this note into document.
Attached is the updated patch. I also fixed my bug which
pg_standby returns 0 even if the requested file fails to be
restored in smart mode.
This patch is ready to commit, I think. Please review this.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
pgstandby_change_trigger_0423.patchapplication/octet-stream; name=pgstandby_change_trigger_0423.patchDownload
Index: contrib/pg_standby/pg_standby.c
===================================================================
RCS file: /projects/cvsroot/pgsql/contrib/pg_standby/pg_standby.c,v
retrieving revision 1.21
diff -c -r1.21 pg_standby.c
*** contrib/pg_standby/pg_standby.c 26 Mar 2009 22:29:13 -0000 1.21
--- contrib/pg_standby/pg_standby.c 23 Apr 2009 12:04:08 -0000
***************
*** 52,58 ****
int keepfiles = 0; /* number of WAL files to keep, 0 keep all */
int maxretries = 3; /* number of retries on restore command */
bool debug = false; /* are we debugging? */
- bool triggered = false; /* have we been triggered? */
bool need_cleanup = false; /* do we need to remove files from
* archive? */
--- 52,57 ----
***************
*** 69,74 ****
--- 68,98 ----
char exclusiveCleanupFileName[MAXPGPATH]; /* the file we need to
* get from archive */
+ /*
+ * Two types of failover are supported (smart and fast failover).
+ *
+ * The content of the trigger file determines the type of failover.
+ * If the trigger file containing "smart" exists, smart failover is chosen;
+ * pg_standby acts as cp or ln command itself, on successful completion
+ * all the available WAL records will be applied resulting in zero data loss.
+ * But, it might take some times before finishing recovery.
+ *
+ * On the other hand, the existence of the trigger file with "fast"
+ * causes recovery to end immediately even if the available WAL files
+ * remain. So, some transactions might be lost.
+ *
+ * An empty trigger file performs smart failover.
+ *
+ * Fast failover is triggered by the signal (SIGUSR1 or SIGINT).
+ *
+ * A timeout causes smart failover.
+ */
+ #define NoFailover 0
+ #define SmartFailover 1
+ #define FastFailover 2
+
+ static int Failover = NoFailover;
+
#define RESTORE_COMMAND_COPY 0
#define RESTORE_COMMAND_LINK 1
int restoreCommandType;
***************
*** 108,114 ****
*
* As an example, and probably the common case, we use either
* cp/ln commands on *nix, or copy/move command on Windows.
- *
*/
static void
CustomizableInitialize(void)
--- 132,137 ----
***************
*** 357,363 ****
static bool
CheckForExternalTrigger(void)
{
! int rc;
/*
* Look for a trigger file, if that option has been selected
--- 380,387 ----
static bool
CheckForExternalTrigger(void)
{
! char buf[32];
! FILE *fd;
/*
* Look for a trigger file, if that option has been selected
***************
*** 365,374 ****
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (triggerPath && stat(triggerPath, &stat_buf) == 0)
{
! fprintf(stderr, "trigger file found\n");
fflush(stderr);
/*
* If trigger file found, we *must* delete it. Here's why: When
--- 389,438 ----
* We use stat() here because triggerPath is always a file rather than
* potentially being in an archive
*/
! if (!triggerPath || stat(triggerPath, &stat_buf) != 0)
! return false;
!
! /*
! * An empty trigger file performs smart failover
! */
! if (stat_buf.st_size == 0)
{
! Failover = SmartFailover;
! fprintf(stderr, "trigger file found: smart failover\n");
fflush(stderr);
+ return true;
+ }
+
+ if ((fd = fopen(triggerPath, "r")) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not open \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ return false;
+ }
+
+ if (fgets(buf, sizeof(buf), fd) == NULL)
+ {
+ fprintf(stderr, "WARNING: could not read \"%s\": %s\n",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ fclose(fd);
+ return false;
+ }
+
+ fclose(fd);
+
+ if (strncmp(buf, "smart", 5) == 0)
+ {
+ Failover = SmartFailover;
+ fprintf(stderr, "trigger file found: smart failover\n");
+ fflush(stderr);
+ return true;
+ }
+
+ if (strncmp(buf, "fast", 4) == 0)
+ {
+ int rc;
/*
* If trigger file found, we *must* delete it. Here's why: When
***************
*** 379,391 ****
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s", triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
return true;
}
!
return false;
}
--- 443,463 ----
rc = unlink(triggerPath);
if (rc != 0)
{
! fprintf(stderr, "\n ERROR: could not remove \"%s\": %s",
! triggerPath, strerror(errno));
fflush(stderr);
exit(rc);
}
+
+ Failover = FastFailover;
+ fprintf(stderr, "trigger file found: fast failover\n");
+ fflush(stderr);
return true;
}
!
! fprintf(stderr, "WARNING: invalid content in \"%s\"\n",
! triggerPath);
! fflush(stderr);
return false;
}
***************
*** 552,559 ****
break;
case 't': /* Trigger file */
triggerPath = optarg;
- if (CheckForExternalTrigger())
- exit(1); /* Normal exit, with non-zero */
break;
case 'w': /* Max wait time */
maxwaittime = atoi(optarg);
--- 624,629 ----
***************
*** 659,664 ****
--- 729,757 ----
strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".history"),
".history") == 0)
{
+ /*
+ * Get rid of the trigger file at the end of archive recovery at least.
+ * Otherwise, it would unexpectedly cause the subsequent warm-standby to
+ * end.
+ *
+ * Here is the right place to remove the trigger file since a timeline
+ * history file is requested only at the beginning and end of archive
+ * recovery.
+ */
+ if (triggerPath && stat(triggerPath, &stat_buf) == 0)
+ {
+ int rc;
+
+ rc = unlink(triggerPath);
+ if (rc != 0)
+ {
+ fprintf(stderr, "\n ERROR: could not remove \"%s\": %s",
+ triggerPath, strerror(errno));
+ fflush(stderr);
+ exit(rc);
+ }
+ }
+
nextWALFileType = XLOG_HISTORY;
if (RestoreWALFileForRecovery())
exit(0);
***************
*** 676,697 ****
/*
* Main wait loop
*/
! while (!CustomizableNextWALFileReady() && !triggered)
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! triggered = true;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit\n");
fflush(stderr);
}
}
else
{
if (debug)
{
--- 769,803 ----
/*
* Main wait loop
*/
! while (!CheckForExternalTrigger() && !CustomizableNextWALFileReady())
{
if (sleeptime <= 60)
pg_usleep(sleeptime * 1000000L);
if (signaled)
{
! Failover = FastFailover;
if (debug)
{
! fprintf(stderr, "\nsignaled to exit: fast failover\n");
fflush(stderr);
}
+ break;
}
else
{
+ waittime += sleeptime;
+ if (waittime >= maxwaittime && maxwaittime > 0)
+ {
+ Failover = FastFailover;
+ if (debug)
+ {
+ fprintf(stderr, "\nTimed out after %d seconds: fast failover\n",
+ waittime);
+ fflush(stderr);
+ }
+ break;
+ }
if (debug)
{
***************
*** 700,722 ****
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
-
- waittime += sleeptime;
-
- if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0)))
- {
- triggered = true;
- if (debug && waittime >= maxwaittime && maxwaittime > 0)
- fprintf(stderr, "\nTimed out after %d seconds\n", waittime);
- }
}
}
/*
* Action on exit
*/
! if (triggered)
! exit(1); /* Normal exit, with non-zero */
/*
* Once we have restored this file successfully we can remove some prior
--- 806,819 ----
fprintf(stderr, " Checking for trigger file...");
fflush(stderr);
}
}
}
/*
* Action on exit
*/
! if (Failover == FastFailover)
! exit(1);
/*
* Once we have restored this file successfully we can remove some prior
***************
*** 724,731 ****
* of them will be requested again immediately after the failed restore,
* or when we restart recovery.
*/
! if (RestoreWALFileForRecovery() && need_cleanup)
! CustomizableCleanupPriorWALFiles();
! return 0;
}
--- 821,833 ----
* of them will be requested again immediately after the failed restore,
* or when we restart recovery.
*/
! if (RestoreWALFileForRecovery())
! {
! if (need_cleanup)
! CustomizableCleanupPriorWALFiles();
! exit(0);
! }
! else
! exit(1);
}
Index: doc/src/sgml/pgstandby.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/pgstandby.sgml,v
retrieving revision 2.7
diff -c -r2.7 pgstandby.sgml
*** doc/src/sgml/pgstandby.sgml 27 Feb 2009 09:30:21 -0000 2.7
--- doc/src/sgml/pgstandby.sgml 23 Apr 2009 12:04:08 -0000
***************
*** 92,97 ****
--- 92,135 ----
is specified,
the <replaceable>archivelocation</> directory must be writable too.
</para>
+ <para>
+ There are two ways to fail over a <quote>warm standby</> database server.
+ You control the type of failover by creating different trigger files
+ (if <literal>-t</> has been specified).
+
+ <variablelist>
+ <varlistentry>
+ <term>Smart Failover</term>
+ <listitem>
+ <para>
+ If a trigger file containing <literal>smart</> or an empty one exists,
+ <application>pg_standby</application> acts as <literal>cp</> or
+ <literal>ln</> command itself, on successful completion all the
+ available WAL records will be applied resulting in zero data loss.
+ But it might take some times before finishing failover.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Fast Failover</term>
+ <listitem>
+ <para>
+ The existence of a trigger file containing <literal>fast</> causes
+ recovery to end immediately even if the available WAL files remain.
+ Though some transactions might be lost, it won't take long before
+ finishing failover.
+ </para>
+ <para>
+ You mustn't copy any extra files into <filename>pg_xlog</> of a
+ <quote>warm standby</> database server if you use a fast trigger.
+ Otherwise, <application>pg_standby</> might get stuck while reading
+ them from <filename>pg_xlog</>. When failover comes to a dead halt
+ unfortunately, it can resume by creating a fast trigger file again.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
<table>
<title><application>pg_standby</> options</title>
***************
*** 177,188 ****
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should cause recovery to end
! whether or not the next WAL file is available.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
<filename>/tmp/pgsql.trigger.5432</>.
</entry>
</row>
<row>
--- 215,227 ----
<entry><literal>-t</> <replaceable>triggerfile</></entry>
<entry>none</entry>
<entry>
! Specify a trigger file whose presence should perform failover.
It is recommended that you use a structured filename to
avoid confusion as to which server is being triggered
when multiple servers exist on the same system; for example
<filename>/tmp/pgsql.trigger.5432</>.
+ Note that a trigger file is deleted at the end of recovery
+ regardless of failover type.
</entry>
</row>
<row>
***************
*** 190,196 ****
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which recovery will end and the standby will come up.
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
--- 229,235 ----
<entry>0</entry>
<entry>
Set the maximum number of seconds to wait for the next WAL file,
! after which a fast failover will be performed.
A setting of zero (the default) means wait forever.
The default setting is not necessarily recommended;
consult <xref linkend="warm-standby"> for discussion.
***************
*** 236,242 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 275,282 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>/tmp/pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
***************
*** 277,283 ****
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears
</para>
</listitem>
<listitem>
--- 317,324 ----
<listitem>
<para>
stop waiting only when a trigger file called
! <filename>C:\pgsql.trigger.5442</> appears,
! and perform failover according to its content
</para>
</listitem>
<listitem>
Fujii Masao wrote:
On Thu, Apr 23, 2009 at 4:49 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:This is getting complicated, though. I guess it would be enough to document
that you mustn't copy any extra files into pg_xlog if you use a fast
trigger.Agreed. I added this note into document.
Attached is the updated patch. I also fixed my bug which
pg_standby returns 0 even if the requested file fails to be
restored in smart mode.This patch is ready to commit, I think. Please review this.
Looking at this again..
Deleting the trigger file when a history file is requested:
/*
* Get rid of the trigger file at the end of archive recovery.
* Otherwise, it would unexpectedly cause the subsequent warm-standby to
* end.
*
* Here is the right place to remove the trigger file since a timeline
* history file is requested only at the beginning and end of archive
* recovery.
*/
changes the behavior in a subtle way: if you create trigger file before
starting recovery, it will be deleted when the recovery is started and
no failover is done. Currently, it will end the recovery immediately.
That makes me uncomfortable to back-patch this. That change in behavior
might be hard to work-around: the process that creates the trigger file
would have to make sure that the server has started recovery before
creating the file.
Here's another idea: Let's modify xlog.c so that when the server asks
for WAL file X, and restore_command returns "not found", the server will
not ask for any WAL files >= X again (in that recovery session).
Presumably if X doesn't exist, no later files will exist either. That
would be pretty simple change, and it would allow us to go with the
simpler implementation in pg_standby and just remove the trigger file
immediately when it returns "not found" (instead of removing it when
history file is requested). That would make it safe to copy extra WAL
files into pg_xlog, even in fast failover mode.
Does anyone see a hole in that idea?
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, 2009-05-12 at 14:15 +0300, Heikki Linnakangas wrote:
Here's another idea: Let's modify xlog.c so that when the server asks
for WAL file X, and restore_command returns "not found", the server
will not ask for any WAL files >= X again (in that recovery session).
Presumably if X doesn't exist, no later files will exist either.
That was proposed and rejected earlier, though not in this context.
Sounds fine to me.
I agree that we should simplify/clarify the file request pattern, which
is the source of many difficulties over last few years.
I would further propose that I take pg_standby out as a module, so we
can more easily support earlier releases.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
On Tue, 2009-05-12 at 14:15 +0300, Heikki Linnakangas wrote:
Here's another idea: Let's modify xlog.c so that when the server asks
for WAL file X, and restore_command returns "not found", the server
will not ask for any WAL files >= X again (in that recovery session).
Presumably if X doesn't exist, no later files will exist either.That was proposed and rejected earlier, though not in this context.
Got a link? I'd like to check the past discussion.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, 2009-05-12 at 14:38 +0300, Heikki Linnakangas wrote:
Simon Riggs wrote:
On Tue, 2009-05-12 at 14:15 +0300, Heikki Linnakangas wrote:
Here's another idea: Let's modify xlog.c so that when the server asks
for WAL file X, and restore_command returns "not found", the server
will not ask for any WAL files >= X again (in that recovery session).
Presumably if X doesn't exist, no later files will exist either.That was proposed and rejected earlier, though not in this context.
Got a link? I'd like to check the past discussion.
I'm sorry, I don't. One of the recovery related discussions started by
me.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
On Tue, May 12, 2009 at 8:15 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
Fujii Masao wrote:
On Thu, Apr 23, 2009 at 4:49 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:This is getting complicated, though. I guess it would be enough to
document
that you mustn't copy any extra files into pg_xlog if you use a fast
trigger.Agreed. I added this note into document.
Attached is the updated patch. I also fixed my bug which
pg_standby returns 0 even if the requested file fails to be
restored in smart mode.This patch is ready to commit, I think. Please review this.
Looking at this again..
Deleting the trigger file when a history file is requested:
/*
* Get rid of the trigger file at the end of archive
recovery.
* Otherwise, it would unexpectedly cause the subsequent
warm-standby to
* end.
*
* Here is the right place to remove the trigger file since
a timeline
* history file is requested only at the beginning and end
of archive
* recovery.
*/changes the behavior in a subtle way: if you create trigger file before
starting recovery, it will be deleted when the recovery is started and no
failover is done. Currently, it will end the recovery immediately.That makes me uncomfortable to back-patch this. That change in behavior
might be hard to work-around: the process that creates the trigger file
would have to make sure that the server has started recovery before creating
the file.Here's another idea: Let's modify xlog.c so that when the server asks for
WAL file X, and restore_command returns "not found", the server will not ask
for any WAL files >= X again (in that recovery session). Presumably if X
doesn't exist, no later files will exist either. That would be pretty simple
change, and it would allow us to go with the simpler implementation in
pg_standby and just remove the trigger file immediately when it returns "not
found" (instead of removing it when history file is requested). That would
make it safe to copy extra WAL files into pg_xlog, even in fast failover
mode.Does anyone see a hole in that idea?
Probably yes. The trigger file would remain after failover if the
restored WAL file has the invalid record which means the end
of WAL. In this case, "not found" is not returned.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Fujii Masao wrote:
On Tue, May 12, 2009 at 8:15 PM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:Here's another idea: Let's modify xlog.c so that when the server asks for
WAL file X, and restore_command returns "not found", the server will not ask
for any WAL files >= X again (in that recovery session). Presumably if X
doesn't exist, no later files will exist either. That would be pretty simple
change, and it would allow us to go with the simpler implementation in
pg_standby and just remove the trigger file immediately when it returns "not
found" (instead of removing it when history file is requested). That would
make it safe to copy extra WAL files into pg_xlog, even in fast failover
mode.Does anyone see a hole in that idea?
Probably yes. The trigger file would remain after failover if the
restored WAL file has the invalid record which means the end
of WAL. In this case, "not found" is not returned.
Yep. That's not pleasant either :-(.
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.
I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.
Does someone want to take a stab at writing a patch for that?
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.
I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.
Does someone want to take a stab at writing a patch for that?
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.
regards, tom lane
On Wed, 2009-05-13 at 13:01 -0400, Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.
Agreed.
I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.
That is the cleanest way, though we cannot really avoid acting for
backbranches also.
Does someone want to take a stab at writing a patch for that?
No, not if there is a likelihood the work would be wasted.
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.
I don't really understand this comment. Why would fixing a memory leak
be worthwhile when fixing a potential for data loss be a deferrable
activity?
I will set-up pg_standby as an external module and we can change it from
there. No more discussions-for-8.4 and I can update as required to
support each release. So let's just remove it from contrib and be done.
Counterthoughts?
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs <simon@2ndQuadrant.com> writes:
I will set-up pg_standby as an external module and we can change it from
there. No more discussions-for-8.4 and I can update as required to
support each release. So let's just remove it from contrib and be done.
Huh? The proposed fix involves a backend change, so I don't see how
removing pg_standby from the distribution frees you from the constraints
of our versioning.
regards, tom lane
Simon Riggs wrote:
I will set-up pg_standby as an external module and we can change it from
there. No more discussions-for-8.4 and I can update as required to
support each release. So let's just remove it from contrib and be done.
Counterthoughts?
We're in Beta. You can't just go yanking stuff like that. Beta testers
will be justifiably very annoyed.
Please calm down.
pg_standby is useful and needs to be correct. And its existence as a
standard module is one of the things that has made me feel confident
about recommending people to use the PITR stuff. I'll be very annoyed if
it were to get pulled.
cheers
andrew
On Wed, 2009-05-13 at 14:14 -0400, Andrew Dunstan wrote:
pg_standby is useful and needs to be correct. And its existence as a
standard module is one of the things that has made me feel confident
about recommending people to use the PITR stuff. I'll be very annoyed if
it were to get pulled.
Although I am not advocating one position or another there are benefits
to removing it. It would be nice to continue to enhance pg_standby
without the limitations of the core release schedule.
Sincerely,
Joshua D. Drake
--
PostgreSQL - XMPP: jdrake@jabber.postgresql.org
Consulting, Development, Support, Training
503-667-4564 - http://www.commandprompt.com/
The PostgreSQL Company, serving since 1997
Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.Does someone want to take a stab at writing a patch for that?
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.
This whole thing can be considered to be a new feature. It's working as
designed. But people seem to be surprised about the current behavior (me
included), and we don't currently provide the behavior that most people
actually want. I think we should fix it for 8.4.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Andrew Dunstan wrote:
We're in Beta. You can't just go yanking stuff like that. Beta testers
will be justifiably very annoyed.Please calm down.
pg_standby is useful and needs to be correct. And its existence as a
standard module is one of the things that has made me feel confident
about recommending people to use the PITR stuff. I'll be very annoyed
if it were to get pulled.
Since mentioned in the docs, I consider it at least the semi-official
tool for pgsql PITR handling. But as this discussion reveals, the api is
flawed, and will not allow guaranteed consistency (whatever pg_standby
tries) until fixed. While this may not be a bug of the restore_script
call, the pitr procedure in total is partially broken (in the sense that
it doesn't provide what most users expect in a secure way) and thus
needs to be fixed. It seems a fix can't be provided without extending
the api.
Regards,
Andreas
Simon Riggs wrote:
On Wed, 2009-05-13 at 13:01 -0400, Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
Does someone want to take a stab at writing a patch for that?
No, not if there is a likelihood the work would be wasted.
There always is.
(I would've wrote the patch myself right away, but I'm extremely busy at
the moment. :-( Might take one more day before I get the time to finish
it, and we don't have much time)
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.I don't really understand this comment. Why would fixing a memory leak
be worthwhile when fixing a potential for data loss be a deferrable
activity?
Because the data loss is working as designed and documented, even though
the design is not what most people want and the documentation could say
that more prominently. That said, I'm in favor of changing this for 8.4.
I will set-up pg_standby as an external module and we can change it from
there. No more discussions-for-8.4 and I can update as required to
support each release. So let's just remove it from contrib and be done.
Counterthoughts?
That's a lot more drastic change to make in beta. Besides, the proposed
fix required backend changes. I think we should keep it in contrib. (At
least for this release: If we get more integrated replication options in
8.5, that would be a good time to move pg_standby out of contrib if
that's what we want.)
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
I think we should fix it for 8.4.
Agreed.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
On Wed, 2009-05-13 at 14:14 -0400, Andrew Dunstan wrote:
pg_standby is useful and needs to be correct.
My suggestion was designed to provide this. A misunderstanding.
And its existence as a
standard module is one of the things that has made me feel confident
about recommending people to use the PITR stuff. I'll be very annoyed if
it were to get pulled.
If we cannot make it correct within core, then I will make it correct
somewhere else, beta or not. Other than that, I have no wish to remove
it from contrib.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
Tom Lane wrote:
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.
This whole thing can be considered to be a new feature. It's working as
designed. But people seem to be surprised about the current behavior (me
included), and we don't currently provide the behavior that most people
actually want. I think we should fix it for 8.4.
Well, that's okay by me if it can be done in a timely fashion. Bear in
mind that we are planning to wrap beta2 not much more than 24 hours from
now.
regards, tom lane
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
That's a lot more drastic change to make in beta. Besides, the proposed
fix required backend changes. I think we should keep it in contrib. (At
least for this release: If we get more integrated replication options in
8.5, that would be a good time to move pg_standby out of contrib if
that's what we want.)
The proposed fix requires coordinated changes in the core and
pg_standby. That would be a lot *harder* if pg_standby were external.
Since we've evidently not gotten this API quite right yet, I think we
should be keeping pg_standby in contrib until we do, ie the API has been
stable for awhile ...
regards, tom lane
Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
That's a lot more drastic change to make in beta. Besides, the proposed
fix required backend changes. I think we should keep it in contrib. (At
least for this release: If we get more integrated replication options in
8.5, that would be a good time to move pg_standby out of contrib if
that's what we want.)The proposed fix requires coordinated changes in the core and
pg_standby. That would be a lot *harder* if pg_standby were external.
Since we've evidently not gotten this API quite right yet, I think we
should be keeping pg_standby in contrib until we do, ie the API has been
stable for awhile ...
Agreed.
Frankly, if anything it should move from contrib to the core proper. I
regard it as an essential utility, not an optional extra.
cheers
andrew
On Wed, 2009-05-13 at 14:53 -0400, Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
Tom Lane wrote:
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.This whole thing can be considered to be a new feature. It's working as
designed. But people seem to be surprised about the current behavior (me
included), and we don't currently provide the behavior that most people
actually want. I think we should fix it for 8.4.Well, that's okay by me if it can be done in a timely fashion. Bear in
mind that we are planning to wrap beta2 not much more than 24 hours from
now.
I'll write it now then, so it can be reviewed tomorrow.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
On Wed, 2009-05-13 at 14:53 -0400, Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
Tom Lane wrote:
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.This whole thing can be considered to be a new feature. It's working as
designed. But people seem to be surprised about the current behavior (me
included), and we don't currently provide the behavior that most people
actually want. I think we should fix it for 8.4.Well, that's okay by me if it can be done in a timely fashion. Bear in
mind that we are planning to wrap beta2 not much more than 24 hours from
now.I'll write it now then, so it can be reviewed tomorrow.
Thanks, Simon!
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Wed, 2009-05-13 at 15:05 -0400, Andrew Dunstan wrote:
Frankly, if anything it should move from contrib to the core proper. I
regard it as an essential utility, not an optional extra.
I like that idea.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
This whole thing can be considered to be a new feature.
recovery.conf will contain a new optional parameter:
recovery_end_command (string)
This parameter specifies a shell command that will be executed once only
at the end of recovery. This parameter is optional. The purpose of the
recovery_end_command is to provide a mechanism for cleanup following
replication or recovery. Any %r is replaced by the name of the file
containing the last valid restart point. That is the earliest file that
must be kept to allow a restore to be restartable, so this information
can be used to truncate the archive to just the minimum required to
support restart of the current restore. %r would only be used in a
warm-standby configuration (see Section 24.4). Write %% to embed an
actual % character in the command.
recovery_end_command is performed *after* the UpdateControlFile() once
the we are DB_IN_PRODUCTION.
This behaviour ensures that a crash prior to the final checkpoint will
continue to see the trigger file. Once we are safe, we can remove the
trigger file safely. We also can now ignore any complexity surrounding
whether WAL files are full or not, and whether WAL files were restored
from the archive or from the local directory.
Comments?
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs <simon@2ndQuadrant.com> writes:
recovery_end_command is performed *after* the UpdateControlFile() once
the we are DB_IN_PRODUCTION.
Hmm, shouldn't it be after the last checkpoint but before we go to
DB_IN_PRODUCTION? I have to admit I've not been following this closely
though, so I may be missing something.
regards, tom lane
On Wed, 2009-05-13 at 16:47 -0400, Tom Lane wrote:
Simon Riggs <simon@2ndQuadrant.com> writes:
recovery_end_command is performed *after* the UpdateControlFile() once
the we are DB_IN_PRODUCTION.Hmm, shouldn't it be after the last checkpoint
Definitely.
but before we go to DB_IN_PRODUCTION?
I think it can be either, so I'll go with your proposal.
(I'm aware Fujii-san is asleep right now, so we should expect another
viewpoint before tomorrow).
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Hi,
Sorry for the delay.
On Thu, May 14, 2009 at 6:04 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
but before we go to DB_IN_PRODUCTION?
I think it can be either, so I'll go with your proposal.
I also think so.
(I'm aware Fujii-san is asleep right now, so we should expect another
viewpoint before tomorrow).
I'd like to avoid adding new parameter for warm-standby
if possible because currently the setup of it is already
complicated. But, I don't have another good idea yet other
than the already proposed. Sorry.
Personally, I'd rather make pg_standby delete a trigger file
when the timeline history file is requested even if this would
break the current behavior, than the setup of warm-standby
becomes more complicated.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Wed, 2009-05-13 at 21:43 +0100, Simon Riggs wrote:
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
This whole thing can be considered to be a new feature.
recovery.conf will contain a new optional parameter:
recovery_end_command (string)
Implemented.
Some possibility of re-factoring in calc of %r, though that has not been
done to ensure code clarity and avoid need for retesting other aspects
of recovery at this stage of beta.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Attachments:
recovery_end_command.v1.patchtext/x-patch; charset=utf-8; name=recovery_end_command.v1.patchDownload
Index: doc/src/sgml/backup.sgml
===================================================================
RCS file: /home/sriggs/pg/REPOSITORY/pgsql/doc/src/sgml/backup.sgml,v
retrieving revision 2.125
diff -c -r2.125 backup.sgml
*** doc/src/sgml/backup.sgml 27 Apr 2009 16:27:35 -0000 2.125
--- doc/src/sgml/backup.sgml 14 May 2009 15:14:35 -0000
***************
*** 1126,1131 ****
--- 1126,1154 ----
</listitem>
</varlistentry>
+ <varlistentry id="recovery-end-command" xreflabel="recovery_end_command">
+ <term><varname>recovery_end_command</varname> (<type>string</type>)</term>
+ <listitem>
+ <para>
+ This parameter specifies a shell command that will be executed once only
+ at the end of recovery. This parameter is optional. The purpose of the
+ recovery_end_command is to provide a mechanism for cleanup following
+ replication or recovery.
+ Any <literal>%r</> is replaced by the name of the file
+ containing the last valid restart point. That is the earliest file that
+ must be kept to allow a restore to be restartable, so this information
+ can be used to truncate the archive to just the minimum required to
+ support restart of the current restore. <literal>%r</> would only be
+ used in a warm-standby configuration (see <xref linkend="warm-standby">).
+ Write <literal>%%</> to embed an actual <literal>%</> character
+ in the command.
+ If the command returns a non-zero exit status then a WARNING log
+ message will be written, unless signalled in which case we return
+ a FATAL error.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="recovery-target-time" xreflabel="recovery_target_time">
<term><varname>recovery_target_time</varname>
(<type>timestamp</type>)
Index: doc/src/sgml/pgstandby.sgml
===================================================================
RCS file: /home/sriggs/pg/REPOSITORY/pgsql/doc/src/sgml/pgstandby.sgml,v
retrieving revision 2.7
diff -c -r2.7 pgstandby.sgml
*** doc/src/sgml/pgstandby.sgml 27 Feb 2009 09:30:21 -0000 2.7
--- doc/src/sgml/pgstandby.sgml 14 May 2009 15:33:18 -0000
***************
*** 210,215 ****
--- 210,216 ----
archive_command = 'cp %p .../archive/%f'
restore_command = 'pg_standby -l -d -s 2 -t /tmp/pgsql.trigger.5442 .../archive %f %p %r 2>>standby.log'
+ recovery_end_command = 'rm /tmp/pgsql.trigger.5442'
</programlisting>
<para>
where the archive directory is physically located on the standby server,
***************
*** 241,246 ****
--- 242,252 ----
</listitem>
<listitem>
<para>
+ remove the trigger file when recovery ends
+ </para>
+ </listitem>
+ <listitem>
+ <para>
remove no-longer-needed files from the archive directory
</para>
</listitem>
Index: src/backend/access/transam/xlog.c
===================================================================
RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/access/transam/xlog.c,v
retrieving revision 1.337
diff -c -r1.337 xlog.c
*** src/backend/access/transam/xlog.c 7 May 2009 11:25:25 -0000 1.337
--- src/backend/access/transam/xlog.c 14 May 2009 15:19:41 -0000
***************
*** 147,152 ****
--- 147,153 ----
/* options taken from recovery.conf */
static char *recoveryRestoreCommand = NULL;
+ static char *recoveryEndCommand = NULL;
static bool recoveryTarget = false;
static bool recoveryTargetExact = false;
static bool recoveryTargetInclusive = true;
***************
*** 463,468 ****
--- 464,470 ----
static void XLogFileClose(void);
static bool RestoreArchivedFile(char *path, const char *xlogfname,
const char *recovername, off_t expectedSize);
+ static void ExecuteRecoveryEndCommand(void);
static void PreallocXlogFiles(XLogRecPtr endptr);
static void RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr);
static void ValidateXLOGDirectoryStructure(void);
***************
*** 2850,2855 ****
--- 2852,2965 ----
}
/*
+ * Attempt to execute the recovery_end_command.
+ */
+ static void
+ ExecuteRecoveryEndCommand(void)
+ {
+ char xlogRecoveryEndCmd[MAXPGPATH];
+ char lastRestartPointFname[MAXPGPATH];
+ char *dp;
+ char *endp;
+ const char *sp;
+ int rc;
+ bool signaled;
+ uint32 restartLog;
+ uint32 restartSeg;
+
+ Assert(recoveryEndCommand);
+
+ /*
+ * Calculate the archive file cutoff point for use during log shipping
+ * replication. All files earlier than this point can be deleted
+ * from the archive, though there is no requirement to do so.
+ *
+ * We initialise this with the filename of an InvalidXLogRecPtr, which
+ * will prevent the deletion of any WAL files from the archive
+ * because of the alphabetic sorting property of WAL filenames.
+ *
+ * Once we have successfully located the redo pointer of the checkpoint
+ * from which we start recovery we never request a file prior to the redo
+ * pointer of the last restartpoint. When redo begins we know that we
+ * have successfully located it, so there is no need for additional
+ * status flags to signify the point when we can begin deleting WAL files
+ * from the archive.
+ */
+ if (InRedo)
+ {
+ XLByteToSeg(ControlFile->checkPointCopy.redo,
+ restartLog, restartSeg);
+ XLogFileName(lastRestartPointFname,
+ ControlFile->checkPointCopy.ThisTimeLineID,
+ restartLog, restartSeg);
+ }
+ else
+ XLogFileName(lastRestartPointFname, 0, 0, 0);
+
+ /*
+ * construct the command to be executed
+ */
+ dp = xlogRecoveryEndCmd;
+ endp = xlogRecoveryEndCmd + MAXPGPATH - 1;
+ *endp = '\0';
+
+ for (sp = recoveryEndCommand; *sp; sp++)
+ {
+ if (*sp == '%')
+ {
+ switch (sp[1])
+ {
+ case 'r':
+ /* %r: filename of last restartpoint */
+ sp++;
+ StrNCpy(dp, lastRestartPointFname, endp - dp);
+ dp += strlen(dp);
+ break;
+ case '%':
+ /* convert %% to a single % */
+ sp++;
+ if (dp < endp)
+ *dp++ = *sp;
+ break;
+ default:
+ /* otherwise treat the % as not special */
+ if (dp < endp)
+ *dp++ = *sp;
+ break;
+ }
+ }
+ else
+ {
+ if (dp < endp)
+ *dp++ = *sp;
+ }
+ }
+ *dp = '\0';
+
+ ereport(DEBUG3,
+ (errmsg_internal("executing recovery end command \"%s\"",
+ xlogRecoveryEndCmd)));
+
+ /*
+ * Copy xlog from archival storage to XLOGDIR
+ */
+ rc = system(xlogRecoveryEndCmd);
+ if (rc != 0)
+ {
+ /*
+ * If the failure was due to any sort of signal, it's best to punt and
+ * abort recovery. See also detailed comments on signals in
+ * RestoreArchivedFile().
+ */
+ signaled = WIFSIGNALED(rc) || WEXITSTATUS(rc) > 125;
+
+ ereport(signaled ? FATAL : WARNING,
+ (errmsg("recovery_end_command \"%s\": return code %d",
+ xlogRecoveryEndCmd, rc)));
+ }
+ }
+
+ /*
* Preallocate log files beyond the specified log endpoint.
*
* XXX this is currently extremely conservative, since it forces only one
***************
*** 4664,4669 ****
--- 4774,4786 ----
(errmsg("restore_command = '%s'",
recoveryRestoreCommand)));
}
+ else if (strcmp(tok1, "recovery_end_command") == 0)
+ {
+ recoveryEndCommand = pstrdup(tok2);
+ ereport(LOG,
+ (errmsg("recovery_end_command = '%s'",
+ recoveryEndCommand)));
+ }
else if (strcmp(tok1, "recovery_target_timeline") == 0)
{
rtliGiven = true;
***************
*** 5622,5627 ****
--- 5739,5747 ----
* allows some extra error checking in xlog_redo.
*/
CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
+
+ if (recoveryEndCommand)
+ ExecuteRecoveryEndCommand();
}
/*
Hi,
On Fri, May 15, 2009 at 12:36 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Wed, 2009-05-13 at 21:43 +0100, Simon Riggs wrote:
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
This whole thing can be considered to be a new feature.
recovery.conf will contain a new optional parameter:
recovery_end_command (string)
Implemented.
+ ereport(signaled ? FATAL : WARNING, + (errmsg("recovery_end_command \"%s\": return code %d", + xlogRecoveryEndCmd, rc)));
In fast failover case, pg_standby has to delete the trigger file immediately
if it's found. Otherwise, recovery may go wrong as I already described.
http://archives.postgresql.org/pgsql-hackers/2009-04/msg01139.php
So, in fast mode, recovery_end_command would always fail to delete the
trigger file, and cause warning. This is odd behavior, I think. We should
change WARNING to DEBUG2 like RestoreArchivedFile() in the above code?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Fujii Masao wrote:
On Fri, May 15, 2009 at 12:36 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Wed, 2009-05-13 at 21:43 +0100, Simon Riggs wrote:
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
This whole thing can be considered to be a new feature.
recovery.conf will contain a new optional parameter:
recovery_end_command (string)
Implemented.
+ ereport(signaled ? FATAL : WARNING, + (errmsg("recovery_end_command \"%s\": return code %d", + xlogRecoveryEndCmd, rc)));In fast failover case, pg_standby has to delete the trigger file immediately
if it's found. Otherwise, recovery may go wrong as I already described.
http://archives.postgresql.org/pgsql-hackers/2009-04/msg01139.php
And if you delete it immediately, you risk getting stuck if there's
extra WAL files in pg_xlog. So still need the change in the backend to
not call restore_command again for a WAL segment equal to or later than
one that it already failed to restore. Or, we can truncate the trigger
file to make it behave like a smart failover for the subsequent
pg_standby calls.
So, in fast mode, recovery_end_command would always fail to delete the
trigger file, and cause warning. This is odd behavior, I think. We should
change WARNING to DEBUG2 like RestoreArchivedFile() in the above code?
I think we should just change the pg_standby example to use "rm -f"
rather than "rm". It seems useful to give a warning if the command fails.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Fri, 2009-05-15 at 03:49 +0900, Fujii Masao wrote:
Hi,
On Fri, May 15, 2009 at 12:36 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Wed, 2009-05-13 at 21:43 +0100, Simon Riggs wrote:
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
This whole thing can be considered to be a new feature.
recovery.conf will contain a new optional parameter:
recovery_end_command (string)
Implemented.
+ ereport(signaled ? FATAL : WARNING, + (errmsg("recovery_end_command \"%s\": return code %d", + xlogRecoveryEndCmd, rc)));In fast failover case, pg_standby has to delete the trigger file immediately
if it's found. Otherwise, recovery may go wrong as I already described.
http://archives.postgresql.org/pgsql-hackers/2009-04/msg01139.phpSo, in fast mode, recovery_end_command would always fail to delete the
trigger file, and cause warning. This is odd behavior, I think. We should
change WARNING to DEBUG2 like RestoreArchivedFile() in the above code?
Using rm -f would avoid the WARNING.
I'd rather keep it at WARNING, since not sure what command I'll be
running and what a non-zero rc means.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
I've finally committed Simon's recovery_end_command patch, as well as
the changes to pg_standby. There's now smart and fast failover modes,
chosen by the content of the trigger file, smart mode is the default. A
"fast" trigger file is truncated, turning it into a "smart" trigger for
subsequent pg_standby invocations. I believe this is now safe in all the
combinations discussed, in both fast and smart mode, with or without
extra WAL files copied to pg_xlog, and also if the last archived WAL
file is incomplete.
You now need to set up recovery_end_command to clean up the trigger
file; pg_standby no longer does that automatically.
Simon Riggs wrote:
On Fri, 2009-05-15 at 03:49 +0900, Fujii Masao wrote:
Hi,
On Fri, May 15, 2009 at 12:36 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
On Wed, 2009-05-13 at 21:43 +0100, Simon Riggs wrote:
On Wed, 2009-05-13 at 21:26 +0300, Heikki Linnakangas wrote:
This whole thing can be considered to be a new feature.
recovery.conf will contain a new optional parameter:
recovery_end_command (string)
Implemented. + ereport(signaled ? FATAL : WARNING, + (errmsg("recovery_end_command \"%s\": return code %d", + xlogRecoveryEndCmd, rc)));In fast failover case, pg_standby has to delete the trigger file immediately
if it's found. Otherwise, recovery may go wrong as I already described.
http://archives.postgresql.org/pgsql-hackers/2009-04/msg01139.phpSo, in fast mode, recovery_end_command would always fail to delete the
trigger file, and cause warning. This is odd behavior, I think. We should
change WARNING to DEBUG2 like RestoreArchivedFile() in the above code?Using rm -f would avoid the WARNING.
I'd rather keep it at WARNING, since not sure what command I'll be
running and what a non-zero rc means.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
Hi,
On Fri, May 15, 2009 at 5:31 AM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
I've finally committed Simon's recovery_end_command patch, as well as the
changes to pg_standby. There's now smart and fast failover modes, chosen by
the content of the trigger file, smart mode is the default. A "fast" trigger
file is truncated, turning it into a "smart" trigger for subsequent
pg_standby invocations. I believe this is now safe in all the combinations
discussed, in both fast and smart mode, with or without extra WAL files
copied to pg_xlog, and also if the last archived WAL file is incomplete.
Thanks for revising my patch and committing it! This seems to work fine
in all the case which I described.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Thu, 2009-05-14 at 23:31 +0300, Heikki Linnakangas wrote:
I've finally committed ....changes to pg_standby.
That was a good team effort. Thanks for committing.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.Does someone want to take a stab at writing a patch for that?
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.
I think the big frustration is that this issue was first brought up
March 25 and it took two months to resolve it, at which point we were in
beta. I think many hoped a better idea would emerge but often that just
doesn't happen and we have to do the best fix we can and move on.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
On Wed, 2009-05-27 at 09:13 -0400, Bruce Momjian wrote:
Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.Does someone want to take a stab at writing a patch for that?
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.I think the big frustration is that this issue was first brought up
March 25 and it took two months to resolve it, at which point we were in
beta. I think many hoped a better idea would emerge but often that just
doesn't happen and we have to do the best fix we can and move on.
I agree, very frustrating. Why do you bring it up again now?
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
On Wed, 2009-05-27 at 09:13 -0400, Bruce Momjian wrote:
Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.Does someone want to take a stab at writing a patch for that?
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.I think the big frustration is that this issue was first brought up
March 25 and it took two months to resolve it, at which point we were in
beta. I think many hoped a better idea would emerge but often that just
doesn't happen and we have to do the best fix we can and move on.I agree, very frustrating. Why do you bring it up again now?
Wny not? We are not going to improve unless we face our faults.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Simon Riggs wrote:
On Wed, 2009-05-27 at 09:13 -0400, Bruce Momjian wrote:
Tom Lane wrote:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
I don't think we're going to get this to work reliably without extending
the interface between the backend and restore_command. We've discussed
many methods and there's always some nasty corner-case like that.I think we should leave back-branches as is, and go with Simon's
suggestion to add new "recovery_end_command" that's run when the
recovery is finished. That's simpler and more reliable than any of the
other approaches we've discussed, and might become handy for other
purposes as well.Does someone want to take a stab at writing a patch for that?
Does this conclusion mean that changing pg_standby is no longer
on the table for 8.4? It certainly smells more like a new feature
than a bug fix.I think the big frustration is that this issue was first brought up
March 25 and it took two months to resolve it, at which point we were in
beta. I think many hoped a better idea would emerge but often that just
doesn't happen and we have to do the best fix we can and move on.I agree, very frustrating. Why do you bring it up again now?
Wny not? We are not going to improve unless we face our faults.
Oh, and I am backlogged on email, which is why I didn't mention it a
week ago when this thread was active (which I think was your point). :-)
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
On Wed, 2009-05-27 at 09:48 -0400, Bruce Momjian wrote:
We are not going to improve unless we face our faults.
True. Who or what is at fault, in your opinion?
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Bruce Momjian wrote:
Bruce Momjian wrote:
Simon Riggs wrote:
On Wed, 2009-05-27 at 09:13 -0400, Bruce Momjian wrote:
I think the big frustration is that this issue was first brought up
March 25 and it took two months to resolve it, at which point we were in
beta. I think many hoped a better idea would emerge but often that just
doesn't happen and we have to do the best fix we can and move on.I agree, very frustrating. Why do you bring it up again now?
Wny not? We are not going to improve unless we face our faults.
Oh, and I am backlogged on email, which is why I didn't mention it a
week ago when this thread was active (which I think was your point). :-)
Did you catch up the backlog far enough to see that Simon coded and I
committed the patch to add "recovery_end_command" just before beta2? So
it's in 8.4 now
(http://archives.postgresql.org/message-id/20090514203109.8EBA2754067@cvs.postgresql.org).
I agree we could've should've handled this more promptly, and I'll take
my part of the blame for that. I let the various proposed patches sit
for long times before reviewing them thoroughly, partly because I was
busy and partly because I didn't feel good about the proposed
approaches. I think what went in in the end is pretty simple and robust,
but it's a shame we had to rush to get it into beta2, after sitting on
our thumbs for months.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Wed, 2009-05-27 at 09:51 -0400, Bruce Momjian wrote:
Bruce Momjian wrote:
Simon Riggs wrote:
On Wed, 2009-05-27 at 09:13 -0400, Bruce Momjian wrote:
I think the big frustration is that this issue was first brought up
March 25 and it took two months to resolve it, at which point we were in
beta. I think many hoped a better idea would emerge but often that just
doesn't happen and we have to do the best fix we can and move on.I agree, very frustrating. Why do you bring it up again now?
Wny not? We are not going to improve unless we face our faults.
Oh, and I am backlogged on email, which is why I didn't mention it a
week ago when this thread was active (which I think was your point). :-)
If there is criticism of someone or something, please let's hear it.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
On Wed, 2009-05-27 at 09:48 -0400, Bruce Momjian wrote:
We are not going to improve unless we face our faults.
True. Who or what is at fault, in your opinion?
Well, we knew there was an issue but we didn't finalize our conclusions
and address it as best we could before beta; instead it languished and
was only addressed when we had our back up against the wall for beta2.
Obviously this is not the best aproach. Ideally someone would have
taken ownership of the issue, summarized the email conclusions, gotten a
patch together, and submitted it for application. I know patches were
posted but no one got group concensus on its usefulness until recently.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
On Wed, 2009-05-27 at 12:08 -0400, Bruce Momjian wrote:
Simon Riggs wrote:
On Wed, 2009-05-27 at 09:48 -0400, Bruce Momjian wrote:
We are not going to improve unless we face our faults.
True. Who or what is at fault, in your opinion?
Well, we knew there was an issue but we didn't finalize our conclusions
and address it as best we could before beta; instead it languished and
was only addressed when we had our back up against the wall for beta2.
Obviously this is not the best aproach. Ideally someone would have
taken ownership of the issue, summarized the email conclusions, gotten a
patch together, and submitted it for application. I know patches were
posted but no one got group consensus on its usefulness until recently.
My experience is that consensus/votes will be overruled by final
committer, if they disagree, and so only a committer has the real
authority to take the role you suggest.
http://en.wiktionary.org/wiki/responsibility
"The obligation to carry forward an assigned task to a successful
conclusion. With responsibility goes authority to direct and take the
necessary action to ensure success"
From my side, Fujii-san's patch was adequate and backpatchable, though
needed docs changes; I proposed it should be committed and said so
clearly on open items wiki. My own suggestion was also an option, but it
was also clearly not backpatchable and seemed unlikely to be acceptable
at any time, let alone during beta. I am surprised at the final outcome,
but at least there is one, which is good.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Simon Riggs wrote:
My experience is that consensus/votes will be overruled by final
committer, if they disagree,
That's a fairly strong statement. I can't think of an occasion when this
has happened on any matter of significance, and I can remember many
times when Tom, Bruce and others have bowed to the consensus despite
their own preferences.
Bruce said the other day that we are trustees of the code, and I don't
think I could put it better. I know I am conscious of that.
cheers
andrew
On Wed, May 27, 2009 at 1:14 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Simon Riggs wrote:
My experience is that consensus/votes will be overruled by final
committer, if they disagree,That's a fairly strong statement. I can't think of an occasion when this has
happened on any matter of significance, and I can remember many times when
Tom, Bruce and others have bowed to the consensus despite their own
preferences.
Tom and Bruce do give way before a clear consensus, but on the other
hand I think Simon is right that there was never much chance of
getting anything committed here without Heikki's endorsement, which
was slow in coming by his own admission. (I'm not in any way saying
he was wrong to withhold his endorsement, just that he did.)
I think it's undeniable that the voices of the committers carry
significantly more weight than those of others on this mailing list,
and especially that of Tom because of the sheer volume of what he
commits compared to anyone else. Having one of the committers say
that they don't like your patch doesn't completely kill its chances of
getting accepted, but it definitely turns it into an uphill battle.
On the other hand, if one of the committers takes a fancy to your
patch it will occasionally jump ahead of the queue and get reviewed or
committed before patches submitted much earlier. And more than one
committer got features into 8.4 that were not really done in time for
the 11/08 CommitFest, and likely would have been rejected if they'd
come from a non-committer.
As a thought experiment, consider two patches, one of which has a +1
from Tom Lane and a -1 from some other respected community member who
is not a committer, and the other of which has a +1 from the community
member and a -1 from Tom Lane. Which do you think is more likely to
get committed? I know what I'd pick.
Now, in many cases, the fact that the committers speak with the
loudest voices is a good thing, because they are mostly very good
coders with lots of PostgreSQL experience and a proven track record of
not breaking the tree too often. But that doesn't make it any less
true.
...Robert
On Wed, 2009-05-27 at 13:14 -0400, Andrew Dunstan wrote:
Simon Riggs wrote:
My experience is that consensus/votes will be overruled by final
committer, if they disagree,That's a fairly strong statement.
I was attempting to be open and honest to allow us to face our faults,
as proposed, because I care about the health and efficiency of the
project. I expected your response and know you think my comments to be
blunt at times. We should worry about the people that don't speak their
mind, not those that do.
I can't think of an occasion when this
has happened on any matter of significance, and I can remember many
times when Tom, Bruce and others have bowed to the consensus despite
their own preferences.
It depends on what you regard as matters of significance and which parts
of the code you pay attention to. Committers can act as they choose; I
am merely pointing out that it isn't possible for non-committers to
follow through on any responsibility they may attempt to assert.
Specifically, trying to propose solutions, achieve consensus and develop
patches only works if the committer will agree with conclusions at the
end and you won't know until you get there. You can do a ton of work and
it can all be for nothing if the consensus opposes the committer. After
a while you reach the conclusion: Why do the work, why not just wait for
the opinion and then act? Less work and same speed.
Bruce said the other day that we are trustees of the code, and I don't
think I could put it better. I know I am conscious of that.
I doubt anyone reading this list feels any differently.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
On Wed, 2009-05-27 at 17:39 +0300, Heikki Linnakangas wrote:
I agree we could've should've handled this more promptly, and I'll take
my part of the blame for that. I let the various proposed patches sit
for long times before reviewing them thoroughly, partly because I was
busy and partly because I didn't feel good about the proposed
approaches. I think what went in in the end is pretty simple and robust,
but it's a shame we had to rush to get it into beta2, after sitting on
our thumbs for months.
Through everything I just said, I have no criticism of you Heikki. I had
the same emotions about the proposals. I was only explaining why I felt
unable to speed up the process myself.
Final patch took me an hour to code and test, so I wasn't put out or
rushed.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
Robert Haas wrote:
Tom and Bruce do give way before a clear consensus, but on the other
hand I think Simon is right that there was never much chance of
getting anything committed here without Heikki's endorsement, which
was slow in coming by his own admission. (I'm not in any way saying
he was wrong to withhold his endorsement, just that he did.)I think it's undeniable that the voices of the committers carry
significantly more weight than those of others on this mailing list,
and especially that of Tom because of the sheer volume of what he
commits compared to anyone else. Having one of the committers say
that they don't like your patch doesn't completely kill its chances of
getting accepted, but it definitely turns it into an uphill battle.
On the other hand, if one of the committers takes a fancy to your
patch it will occasionally jump ahead of the queue and get reviewed or
committed before patches submitted much earlier. And more than one
committer got features into 8.4 that were not really done in time for
the 11/08 CommitFest, and likely would have been rejected if they'd
come from a non-committer.As a thought experiment, consider two patches, one of which has a +1
from Tom Lane and a -1 from some other respected community member who
is not a committer, and the other of which has a +1 from the community
member and a -1 from Tom Lane. Which do you think is more likely to
get committed? I know what I'd pick.Now, in many cases, the fact that the committers speak with the
loudest voices is a good thing, because they are mostly very good
coders with lots of PostgreSQL experience and a proven track record of
not breaking the tree too often. But that doesn't make it any less
true.
The above comments by Robert are very perceptive, and there is certainly
truth that committer-endorsed patches are applied quicker than others.
My only additional comment is that over time, reliable patch submitters
become committers, so hopefully things balance out over time.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
On Wed, 2009-05-27 at 12:08 -0400, Bruce Momjian wrote:
Ideally someone would have
taken ownership of the issue, summarized the email conclusions, gotten
a patch together, and submitted it for application.
Just a further comment on this, based upon the patch Heikki recently
committed.
I raised various issues with recovery *after* feature freeze in 8.3,
doing everything you mentioned above: patch (Jun '07), 4 months before
beta1. 3.5 months later the patch was still un-reviewed and you deferred
the patch until 8.4, without comment (Sep '07). Changes were eventually
committed more than a year after original discussion (Apr '08).
HACKERS "Minor changes to recovery related code"
My other comments relate to that experience, and others.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support