Remove exclusive backup mode

Exclusive-mode backups have been deprecated since 9.6 (when
non-exclusive backups were introduced) due to the issues
they can cause should the system crash while one is running and
generally because non-exclusive provides a much better interface.
Further, exclusive backup mode wasn't really being tested (nor was most
of the related code- like being able to log in just to stop an exclusive
backup and the bits of the state machine related to that) and having to
possibly deal with an exclusive backup and the backup_label file
existing during pg_basebackup, pg_rewind, etc, added other complexities
that we are better off without.

This patch removes the exclusive backup mode, the various special cases
for dealing with it, and greatly simplifies the online backup code and
documentation.

Authors: David Steele, Nathan Bossart
Reviewed-by: Chapman Flack
Discussion: https://postgr.es/m/ac7339ca-3718-3c93-929f-99e725d1172c@pgmasters.net
https://postgr.es/m/CAHg+QDfiM+WU61tF6=nPZocMZvHDzCK47Kneyb0ZRULYzV5sKQ@mail.gmail.com
This commit is contained in:
Stephen Frost 2022-04-06 14:41:03 -04:00
parent 14d3f24fa8
commit 39969e2a1e
24 changed files with 246 additions and 1199 deletions

View File

@ -857,18 +857,9 @@ test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_wal/0
sequence, and that the success of a step is verified before sequence, and that the success of a step is verified before
proceeding to the next step. proceeding to the next step.
</para> </para>
<para>
Low level base backups can be made in a non-exclusive or an exclusive
way. The non-exclusive method is recommended and the exclusive one is
deprecated and will eventually be removed.
</para>
<sect3 id="backup-lowlevel-base-backup-nonexclusive">
<title>Making a Non-Exclusive Low-Level Backup</title>
<para> <para>
A non-exclusive low level backup is one that allows other Multiple backups are able to be run concurrently (both those
concurrent backups to be running (both those started using started using this backup API and those started using
the same backup API and those started using
<xref linkend="app-pgbasebackup"/>). <xref linkend="app-pgbasebackup"/>).
</para> </para>
<para> <para>
@ -881,34 +872,30 @@ test ! -f /mnt/server/archivedir/00000001000000A900000065 &amp;&amp; cp pg_wal/0
<listitem> <listitem>
<para> <para>
Connect to the server (it does not matter which database) as a user with Connect to the server (it does not matter which database) as a user with
rights to run pg_start_backup (superuser, or a user who has been granted rights to run pg_backup_start (superuser, or a user who has been granted
EXECUTE on the function) and issue the command: EXECUTE on the function) and issue the command:
<programlisting> <programlisting>
SELECT pg_start_backup('label', false, false); SELECT pg_backup_start(label => 'label', fast => false);
</programlisting> </programlisting>
where <literal>label</literal> is any string you want to use to uniquely where <literal>label</literal> is any string you want to use to uniquely
identify this backup operation. The connection identify this backup operation. The connection
calling <function>pg_start_backup</function> must be maintained until the end of calling <function>pg_backup_start</function> must be maintained until the end of
the backup, or the backup will be automatically aborted. the backup, or the backup will be automatically aborted.
</para> </para>
<para> <para>
By default, <function>pg_start_backup</function> can take a long time to finish. Online backups are always started at the beginning of a checkpoint.
This is because it performs a checkpoint, and the I/O By default, <function>pg_backup_start</function> will wait for the next
required for the checkpoint will be spread out over a significant regularly scheduled checkpoint to complete, which may take a long time (see the
period of time, by default half your inter-checkpoint interval configuration parameters <xref linkend="guc-checkpoint-timeout"/> and
(see the configuration parameter
<xref linkend="guc-checkpoint-completion-target"/>). This is <xref linkend="guc-checkpoint-completion-target"/>). This is
usually what you want, because it minimizes the impact on query usually preferrable as it minimizes the impact on the running system. If you
processing. If you want to start the backup as soon as want to start the backup as soon as possible, pass <literal>true</literal> as
possible, change the second parameter to <literal>true</literal>, which will the second parameter to <function>pg_backup_start</function> and it will
issue an immediate checkpoint using as much I/O as available. request an immediate checkpoint, which will finish as fast as possible using
as much I/O as possible.
</para> </para>
<para>
The third parameter being <literal>false</literal> tells
<function>pg_start_backup</function> to initiate a non-exclusive base backup.
</para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
@ -926,7 +913,7 @@ SELECT pg_start_backup('label', false, false);
<para> <para>
In the same connection as before, issue the command: In the same connection as before, issue the command:
<programlisting> <programlisting>
SELECT * FROM pg_stop_backup(false, true); SELECT * FROM pg_backup_stop(wait_for_archive => true);
</programlisting> </programlisting>
This terminates backup mode. On a primary, it also performs an automatic This terminates backup mode. On a primary, it also performs an automatic
switch to the next WAL segment. On a standby, it is not possible to switch to the next WAL segment. On a standby, it is not possible to
@ -937,7 +924,7 @@ SELECT * FROM pg_stop_backup(false, true);
ready to archive. ready to archive.
</para> </para>
<para> <para>
The <function>pg_stop_backup</function> will return one row with three <function>pg_backup_stop</function> will return one row with three
values. The second of these fields should be written to a file named values. The second of these fields should be written to a file named
<filename>backup_label</filename> in the root directory of the backup. The <filename>backup_label</filename> in the root directory of the backup. The
third field should be written to a file named third field should be written to a file named
@ -949,14 +936,14 @@ SELECT * FROM pg_stop_backup(false, true);
<listitem> <listitem>
<para> <para>
Once the WAL segment files active during the backup are archived, you are Once the WAL segment files active during the backup are archived, you are
done. The file identified by <function>pg_stop_backup</function>'s first return done. The file identified by <function>pg_backup_stop</function>'s first return
value is the last segment that is required to form a complete set of value is the last segment that is required to form a complete set of
backup files. On a primary, if <varname>archive_mode</varname> is enabled and the backup files. On a primary, if <varname>archive_mode</varname> is enabled and the
<literal>wait_for_archive</literal> parameter is <literal>true</literal>, <literal>wait_for_archive</literal> parameter is <literal>true</literal>,
<function>pg_stop_backup</function> does not return until the last segment has <function>pg_backup_stop</function> does not return until the last segment has
been archived. been archived.
On a standby, <varname>archive_mode</varname> must be <literal>always</literal> in order On a standby, <varname>archive_mode</varname> must be <literal>always</literal> in order
for <function>pg_stop_backup</function> to wait. for <function>pg_backup_stop</function> to wait.
Archiving of these files happens automatically since you have Archiving of these files happens automatically since you have
already configured <varname>archive_library</varname>. In most cases this already configured <varname>archive_library</varname>. In most cases this
happens quickly, but you are advised to monitor your archive happens quickly, but you are advised to monitor your archive
@ -965,9 +952,9 @@ SELECT * FROM pg_stop_backup(false, true);
because of failures of the archive library, it will keep retrying because of failures of the archive library, it will keep retrying
until the archive succeeds and the backup is complete. until the archive succeeds and the backup is complete.
If you wish to place a time limit on the execution of If you wish to place a time limit on the execution of
<function>pg_stop_backup</function>, set an appropriate <function>pg_backup_stop</function>, set an appropriate
<varname>statement_timeout</varname> value, but make note that if <varname>statement_timeout</varname> value, but make note that if
<function>pg_stop_backup</function> terminates because of this your backup <function>pg_backup_stop</function> terminates because of this your backup
may not be valid. may not be valid.
</para> </para>
<para> <para>
@ -975,8 +962,8 @@ SELECT * FROM pg_stop_backup(false, true);
required for the backup are successfully archived then the required for the backup are successfully archived then the
<literal>wait_for_archive</literal> parameter (which defaults to true) can be set <literal>wait_for_archive</literal> parameter (which defaults to true) can be set
to false to have to false to have
<function>pg_stop_backup</function> return as soon as the stop backup record is <function>pg_backup_stop</function> return as soon as the stop backup record is
written to the WAL. By default, <function>pg_stop_backup</function> will wait written to the WAL. By default, <function>pg_backup_stop</function> will wait
until all WAL has been archived, which can take some time. This option until all WAL has been archived, which can take some time. This option
must be used with caution: if WAL archiving is not monitored correctly must be used with caution: if WAL archiving is not monitored correctly
then the backup might not include all of the WAL files and will then the backup might not include all of the WAL files and will
@ -985,142 +972,6 @@ SELECT * FROM pg_stop_backup(false, true);
</listitem> </listitem>
</orderedlist> </orderedlist>
</para> </para>
</sect3>
<sect3 id="backup-lowlevel-base-backup-exclusive">
<title>Making an Exclusive Low-Level Backup</title>
<note>
<para>
The exclusive backup method is deprecated and should be avoided.
Prior to <productname>PostgreSQL</productname> 9.6, this was the only
low-level method available, but it is now recommended that all users
upgrade their scripts to use non-exclusive backups.
</para>
</note>
<para>
The process for an exclusive backup is mostly the same as for a
non-exclusive one, but it differs in a few key steps. This type of
backup can only be taken on a primary and does not allow concurrent
backups. Moreover, because it creates a backup label file, as
described below, it can block automatic restart of the primary server
after a crash. On the other hand, the erroneous removal of this
file from a backup or standby is a common mistake, which can result
in serious data corruption. If it is necessary to use this method,
the following steps may be used.
</para>
<para>
<orderedlist>
<listitem>
<para>
Ensure that WAL archiving is enabled and working.
</para>
</listitem>
<listitem>
<para>
Connect to the server (it does not matter which database) as a user with
rights to run pg_start_backup (superuser, or a user who has been granted
EXECUTE on the function) and issue the command:
<programlisting>
SELECT pg_start_backup('label');
</programlisting>
where <literal>label</literal> is any string you want to use to uniquely
identify this backup operation.
<function>pg_start_backup</function> creates a <firstterm>backup label</firstterm> file,
called <filename>backup_label</filename>, in the cluster directory with
information about your backup, including the start time and label string.
The function also creates a <firstterm>tablespace map</firstterm> file,
called <filename>tablespace_map</filename>, in the cluster directory with
information about tablespace symbolic links in <filename>pg_tblspc/</filename> if
one or more such link is present. Both files are critical to the
integrity of the backup, should you need to restore from it.
</para>
<para>
By default, <function>pg_start_backup</function> can take a long time to finish.
This is because it performs a checkpoint, and the I/O
required for the checkpoint will be spread out over a significant
period of time, by default half your inter-checkpoint interval
(see the configuration parameter
<xref linkend="guc-checkpoint-completion-target"/>). This is
usually what you want, because it minimizes the impact on query
processing. If you want to start the backup as soon as
possible, use:
<programlisting>
SELECT pg_start_backup('label', true);
</programlisting>
This forces the checkpoint to be done as quickly as possible.
</para>
</listitem>
<listitem>
<para>
Perform the backup, using any convenient file-system-backup tool
such as <application>tar</application> or <application>cpio</application> (not
<application>pg_dump</application> or
<application>pg_dumpall</application>). It is neither
necessary nor desirable to stop normal operation of the database
while you do this. See
<xref linkend="backup-lowlevel-base-backup-data"/> for things to
consider during this backup.
</para>
<para>
As noted above, if the server crashes during the backup it may not be
possible to restart until the <filename>backup_label</filename> file has
been manually deleted from the <envar>PGDATA</envar> directory. Note
that it is very important to never remove the
<filename>backup_label</filename> file when restoring a backup, because
this will result in corruption. Confusion about when it is appropriate
to remove this file is a common cause of data corruption when using this
method; be very certain that you remove the file only on an existing
primary and never when building a standby or restoring a backup, even if
you are building a standby that will subsequently be promoted to a new
primary.
</para>
</listitem>
<listitem>
<para>
Again connect to the database as a user with rights to run
pg_stop_backup (superuser, or a user who has been granted EXECUTE on
the function), and issue the command:
<programlisting>
SELECT pg_stop_backup();
</programlisting>
This function terminates backup mode and
performs an automatic switch to the next WAL segment. The reason for the
switch is to arrange for the last WAL segment written during the backup
interval to be ready to archive.
</para>
</listitem>
<listitem>
<para>
Once the WAL segment files active during the backup are archived, you are
done. The file identified by <function>pg_stop_backup</function>'s result is
the last segment that is required to form a complete set of backup files.
If <varname>archive_mode</varname> is enabled,
<function>pg_stop_backup</function> does not return until the last segment has
been archived.
Archiving of these files happens automatically since you have
already configured <varname>archive_command</varname>. In most cases this
happens quickly, but you are advised to monitor your archive
system to ensure there are no delays.
If the archive process has fallen behind
because of failures of the archive command, it will keep retrying
until the archive succeeds and the backup is complete.
</para>
<para>
When using exclusive backup mode, it is absolutely imperative to ensure
that <function>pg_stop_backup</function> completes successfully at the
end of the backup. Even if the backup itself fails, for example due to
lack of disk space, failure to call <function>pg_stop_backup</function>
will leave the server in backup mode indefinitely, causing future backups
to fail and increasing the risk of a restart failure during the time that
<filename>backup_label</filename> exists.
</para>
</listitem>
</orderedlist>
</para>
</sect3>
<sect3 id="backup-lowlevel-base-backup-data"> <sect3 id="backup-lowlevel-base-backup-data">
<title>Backing Up the Data Directory</title> <title>Backing Up the Data Directory</title>
<para> <para>
@ -1203,8 +1054,8 @@ SELECT pg_stop_backup();
<para> <para>
The backup label The backup label
file includes the label string you gave to <function>pg_start_backup</function>, file includes the label string you gave to <function>pg_backup_start</function>,
as well as the time at which <function>pg_start_backup</function> was run, and as well as the time at which <function>pg_backup_start</function> was run, and
the name of the starting WAL file. In case of confusion it is therefore the name of the starting WAL file. In case of confusion it is therefore
possible to look inside a backup file and determine exactly which possible to look inside a backup file and determine exactly which
backup session the dump file came from. The tablespace map file includes backup session the dump file came from. The tablespace map file includes
@ -1218,7 +1069,7 @@ SELECT pg_stop_backup();
<para> <para>
It is also possible to make a backup while the server is It is also possible to make a backup while the server is
stopped. In this case, you obviously cannot use stopped. In this case, you obviously cannot use
<function>pg_start_backup</function> or <function>pg_stop_backup</function>, and <function>pg_backup_start</function> or <function>pg_backup_stop</function>, and
you will therefore be left to your own devices to keep track of which you will therefore be left to your own devices to keep track of which
backup is which and how far back the associated WAL files go. backup is which and how far back the associated WAL files go.
It is generally better to follow the continuous archiving procedure above. It is generally better to follow the continuous archiving procedure above.
@ -1393,7 +1244,7 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
<note> <note>
<para> <para>
The stop point must be after the ending time of the base backup, i.e., The stop point must be after the ending time of the base backup, i.e.,
the end time of <function>pg_stop_backup</function>. You cannot use a base backup the end time of <function>pg_backup_stop</function>. You cannot use a base backup
to recover to a time when that backup was in progress. (To to recover to a time when that backup was in progress. (To
recover to such a time, you must go back to your previous base backup recover to such a time, you must go back to your previous base backup
and roll forward from there.) and roll forward from there.)
@ -1513,44 +1364,6 @@ restore_command = 'cp /mnt/server/archivedir/%f %p'
included in the backup automatically, and no special action is included in the backup automatically, and no special action is
required to restore the backup. required to restore the backup.
</para> </para>
<para>
If more flexibility in copying the backup files is needed, a lower
level process can be used for standalone hot backups as well.
To prepare for low level standalone hot backups, make sure
<varname>wal_level</varname> is set to
<literal>replica</literal> or higher, <varname>archive_mode</varname> to
<literal>on</literal>, and set up an <varname>archive_library</varname> that performs
archiving only when a <emphasis>switch file</emphasis> exists. For example:
<programlisting>
archive_library = '' # use shell command
archive_command = 'test ! -f /var/lib/pgsql/backup_in_progress || (test ! -f /var/lib/pgsql/archive/%f &amp;&amp; cp %p /var/lib/pgsql/archive/%f)'
</programlisting>
This command will perform archiving when
<filename>/var/lib/pgsql/backup_in_progress</filename> exists, and otherwise
silently return zero exit status (allowing <productname>PostgreSQL</productname>
to recycle the unwanted WAL file).
</para>
<para>
With this preparation, a backup can be taken using a script like the
following:
<programlisting>
touch /var/lib/pgsql/backup_in_progress
psql -c "select pg_start_backup('hot_backup');"
tar -cf /var/lib/pgsql/backup.tar /var/lib/pgsql/data/
psql -c "select pg_stop_backup();"
rm /var/lib/pgsql/backup_in_progress
tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/
</programlisting>
The switch file <filename>/var/lib/pgsql/backup_in_progress</filename> is
created first, enabling archiving of completed WAL files to occur.
After the backup the switch file is removed. Archived WAL files are
then added to the backup so that both base backup and all required
WAL files are part of the same <application>tar</application> file.
Please remember to add error handling to your backup scripts.
</para>
</sect3> </sect3>
<sect3 id="compressed-archive-logs"> <sect3 id="compressed-archive-logs">

View File

@ -25618,9 +25618,8 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
The functions shown in <xref The functions shown in <xref
linkend="functions-admin-backup-table"/> assist in making on-line backups. linkend="functions-admin-backup-table"/> assist in making on-line backups.
These functions cannot be executed during recovery (except These functions cannot be executed during recovery (except
non-exclusive <function>pg_start_backup</function>, <function>pg_backup_start</function>,
non-exclusive <function>pg_stop_backup</function>, <function>pg_backup_stop</function>,
<function>pg_is_in_backup</function>, <function>pg_backup_start_time</function>
and <function>pg_wal_lsn_diff</function>). and <function>pg_wal_lsn_diff</function>).
</para> </para>
@ -25709,13 +25708,12 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
<row> <row>
<entry role="func_table_entry"><para role="func_signature"> <entry role="func_table_entry"><para role="func_signature">
<indexterm> <indexterm>
<primary>pg_start_backup</primary> <primary>pg_backup_start</primary>
</indexterm> </indexterm>
<function>pg_start_backup</function> ( <function>pg_backup_start</function> (
<parameter>label</parameter> <type>text</type> <parameter>label</parameter> <type>text</type>
<optional>, <parameter>fast</parameter> <type>boolean</type> <optional>, <parameter>fast</parameter> <type>boolean</type>
<optional>, <parameter>exclusive</parameter> <type>boolean</type> </optional> )
</optional></optional> )
<returnvalue>pg_lsn</returnvalue> <returnvalue>pg_lsn</returnvalue>
</para> </para>
<para> <para>
@ -25724,23 +25722,9 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
(Typically this would be the name under which the backup dump file (Typically this would be the name under which the backup dump file
will be stored.) will be stored.)
If the optional second parameter is given as <literal>true</literal>, If the optional second parameter is given as <literal>true</literal>,
it specifies executing <function>pg_start_backup</function> as quickly it specifies executing <function>pg_backup_start</function> as quickly
as possible. This forces an immediate checkpoint which will cause a as possible. This forces an immediate checkpoint which will cause a
spike in I/O operations, slowing any concurrently executing queries. spike in I/O operations, slowing any concurrently executing queries.
The optional third parameter specifies whether to perform an exclusive
or non-exclusive backup (default is exclusive).
</para>
<para>
When used in exclusive mode, this function writes a backup label file
(<filename>backup_label</filename>) and, if there are any links in
the <filename>pg_tblspc/</filename> directory, a tablespace map file
(<filename>tablespace_map</filename>) into the database cluster's data
directory, then performs a checkpoint, and then returns the backup's
starting write-ahead log location. (The user can ignore this
result value, but it is provided in case it is useful.) When used in
non-exclusive mode, the contents of these files are instead returned
by the <function>pg_stop_backup</function> function, and should be
copied to the backup area by the user.
</para> </para>
<para> <para>
This function is restricted to superusers by default, but other users This function is restricted to superusers by default, but other users
@ -25751,11 +25735,10 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
<row> <row>
<entry role="func_table_entry"><para role="func_signature"> <entry role="func_table_entry"><para role="func_signature">
<indexterm> <indexterm>
<primary>pg_stop_backup</primary> <primary>pg_backup_stop</primary>
</indexterm> </indexterm>
<function>pg_stop_backup</function> ( <function>pg_backup_stop</function> (
<parameter>exclusive</parameter> <type>boolean</type> <optional><parameter>wait_for_archive</parameter> <type>boolean</type>
<optional>, <parameter>wait_for_archive</parameter> <type>boolean</type>
</optional> ) </optional> )
<returnvalue>record</returnvalue> <returnvalue>record</returnvalue>
( <parameter>lsn</parameter> <type>pg_lsn</type>, ( <parameter>lsn</parameter> <type>pg_lsn</type>,
@ -25763,24 +25746,21 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
<parameter>spcmapfile</parameter> <type>text</type> ) <parameter>spcmapfile</parameter> <type>text</type> )
</para> </para>
<para> <para>
Finishes performing an exclusive or non-exclusive on-line backup. Finishes performing an on-line backup. The desired contents of the
The <parameter>exclusive</parameter> parameter must match the backup label file and the tablespace map file are returned as part of
previous <function>pg_start_backup</function> call. the result of the function and must be written to files in the
In an exclusive backup, <function>pg_stop_backup</function> removes backup area. These files must not be written to the live data directory
the backup label file and, if it exists, the tablespace map file (doing so will cause PostgreSQL to fail to restart in the event of a
created by <function>pg_start_backup</function>. In a non-exclusive crash).
backup, the desired contents of these files are returned as part of
the result of the function, and should be written to files in the
backup area (not in the data directory).
</para> </para>
<para> <para>
There is an optional second parameter of type <type>boolean</type>. There is an optional parameter of type <type>boolean</type>.
If false, the function will return immediately after the backup is If false, the function will return immediately after the backup is
completed, without waiting for WAL to be archived. This behavior is completed, without waiting for WAL to be archived. This behavior is
only useful with backup software that independently monitors WAL only useful with backup software that independently monitors WAL
archiving. Otherwise, WAL required to make the backup consistent might archiving. Otherwise, WAL required to make the backup consistent might
be missing and make the backup useless. By default or when this be missing and make the backup useless. By default or when this
parameter is true, <function>pg_stop_backup</function> will wait for parameter is true, <function>pg_backup_stop</function> will wait for
WAL to be archived when archiving is enabled. (On a standby, this WAL to be archived when archiving is enabled. (On a standby, this
means that it will wait only when <varname>archive_mode</varname> = means that it will wait only when <varname>archive_mode</varname> =
<literal>always</literal>. If write activity on the primary is low, <literal>always</literal>. If write activity on the primary is low,
@ -25790,7 +25770,7 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
<para> <para>
When executed on a primary, this function also creates a backup When executed on a primary, this function also creates a backup
history file in the write-ahead log archive area. The history file history file in the write-ahead log archive area. The history file
includes the label given to <function>pg_start_backup</function>, the includes the label given to <function>pg_backup_start</function>, the
starting and ending write-ahead log locations for the backup, and the starting and ending write-ahead log locations for the backup, and the
starting and ending times of the backup. After recording the ending starting and ending times of the backup. After recording the ending
location, the current write-ahead log insertion point is automatically location, the current write-ahead log insertion point is automatically
@ -25801,10 +25781,11 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
<para> <para>
The result of the function is a single record. The result of the function is a single record.
The <parameter>lsn</parameter> column holds the backup's ending The <parameter>lsn</parameter> column holds the backup's ending
write-ahead log location (which again can be ignored). The second and write-ahead log location (which again can be ignored). The second
third columns are <literal>NULL</literal> when ending an exclusive column returns the contents of the backup label file, and the third
backup; after a non-exclusive backup they hold the desired contents of column returns the contents of the tablespace map file. These must be
the label and tablespace map files. stored as part of the backup and are required as part of the restore
process.
</para> </para>
<para> <para>
This function is restricted to superusers by default, but other users This function is restricted to superusers by default, but other users
@ -25812,50 +25793,6 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
</para></entry> </para></entry>
</row> </row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<function>pg_stop_backup</function> ()
<returnvalue>pg_lsn</returnvalue>
</para>
<para>
Finishes performing an exclusive on-line backup. This simplified
version is equivalent to <literal>pg_stop_backup(true,
true)</literal>, except that it only returns the <type>pg_lsn</type>
result.
</para>
<para>
This function is restricted to superusers by default, but other users
can be granted EXECUTE to run the function.
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>pg_is_in_backup</primary>
</indexterm>
<function>pg_is_in_backup</function> ()
<returnvalue>boolean</returnvalue>
</para>
<para>
Returns true if an on-line exclusive backup is in progress.
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>pg_backup_start_time</primary>
</indexterm>
<function>pg_backup_start_time</function> ()
<returnvalue>timestamp with time zone</returnvalue>
</para>
<para>
Returns the start time of the current on-line exclusive backup if one
is in progress, otherwise <literal>NULL</literal>.
</para></entry>
</row>
<row> <row>
<entry role="func_table_entry"><para role="func_signature"> <entry role="func_table_entry"><para role="func_signature">
<indexterm> <indexterm>
@ -25953,7 +25890,7 @@ LOG: Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
corresponding write-ahead log file name and byte offset from corresponding write-ahead log file name and byte offset from
a <type>pg_lsn</type> value. For example: a <type>pg_lsn</type> value. For example:
<programlisting> <programlisting>
postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup()); postgres=# SELECT * FROM pg_walfile_name_offset((pg_backup_stop()).lsn);
file_name | file_offset file_name | file_offset
--------------------------+------------- --------------------------+-------------
00000001000000000000000D | 4039624 00000001000000000000000D | 4039624

View File

@ -1361,8 +1361,8 @@ synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
<para> <para>
If you need to re-create a standby server while transactions are If you need to re-create a standby server while transactions are
waiting, make sure that the commands pg_start_backup() and waiting, make sure that the commands pg_backup_start() and
pg_stop_backup() are run in a session with pg_backup_stop() are run in a session with
<varname>synchronous_commit</varname> = <literal>off</literal>, otherwise those <varname>synchronous_commit</varname> = <literal>off</literal>, otherwise those
requests will wait forever for the standby to appear. requests will wait forever for the standby to appear.
</para> </para>
@ -2159,7 +2159,7 @@ HINT: You can then restart the server after making the necessary configuration
<para> <para>
WAL file control commands will not work during recovery, WAL file control commands will not work during recovery,
e.g., <function>pg_start_backup</function>, <function>pg_switch_wal</function> etc. e.g., <function>pg_backup_start</function>, <function>pg_switch_wal</function> etc.
</para> </para>
<para> <para>

View File

@ -6674,7 +6674,7 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
<entry><literal>waiting for checkpoint to finish</literal></entry> <entry><literal>waiting for checkpoint to finish</literal></entry>
<entry> <entry>
The WAL sender process is currently performing The WAL sender process is currently performing
<function>pg_start_backup</function> to prepare to <function>pg_backup_start</function> to prepare to
take a base backup, and waiting for the start-of-backup take a base backup, and waiting for the start-of-backup
checkpoint to finish. checkpoint to finish.
</entry> </entry>
@ -6697,7 +6697,7 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
<entry><literal>waiting for wal archiving to finish</literal></entry> <entry><literal>waiting for wal archiving to finish</literal></entry>
<entry> <entry>
The WAL sender process is currently performing The WAL sender process is currently performing
<function>pg_stop_backup</function> to finish the backup, <function>pg_backup_stop</function> to finish the backup,
and waiting for all the WAL files required for the base backup and waiting for all the WAL files required for the base backup
to be successfully archived. to be successfully archived.
If either <literal>--wal-method=none</literal> or If either <literal>--wal-method=none</literal> or

View File

@ -186,11 +186,11 @@ PostgreSQL documentation
the specified data directory. Three different the specified data directory. Three different
shutdown methods can be selected with the <option>-m</option> shutdown methods can be selected with the <option>-m</option>
option. <quote>Smart</quote> mode disallows new connections, then waits option. <quote>Smart</quote> mode disallows new connections, then waits
for all existing clients to disconnect and any online backup to finish. for all existing clients to disconnect.
If the server is in hot standby, recovery and streaming replication If the server is in hot standby, recovery and streaming replication
will be terminated once all clients have disconnected. will be terminated once all clients have disconnected.
<quote>Fast</quote> mode (the default) does not wait for clients to disconnect and <quote>Fast</quote> mode (the default) does not wait for clients to disconnect.
will terminate an online backup in progress. All active transactions are All active transactions are
rolled back and clients are forcibly disconnected, then the rolled back and clients are forcibly disconnected, then the
server is shut down. <quote>Immediate</quote> mode will abort server is shut down. <quote>Immediate</quote> mode will abort
all server processes immediately, without a clean shutdown. This choice all server processes immediately, without a clean shutdown. This choice

View File

@ -618,7 +618,7 @@ rsync --archive --delete --hard-links --size-only --no-inc-recursive /vol1/pg_tb
<para> <para>
Configure the servers for log shipping. (You do not need to run Configure the servers for log shipping. (You do not need to run
<function>pg_start_backup()</function> and <function>pg_stop_backup()</function> <function>pg_backup_start()</function> and <function>pg_backup_stop()</function>
or take a file system backup as the standbys are still synchronized or take a file system backup as the standbys are still synchronized
with the primary.) with the primary.)
</para> </para>

View File

@ -1552,11 +1552,7 @@ $ <userinput>cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages</userinp
After receiving <systemitem>SIGTERM</systemitem>, the server After receiving <systemitem>SIGTERM</systemitem>, the server
disallows new connections, but lets existing sessions end their disallows new connections, but lets existing sessions end their
work normally. It shuts down only after all of the sessions terminate. work normally. It shuts down only after all of the sessions terminate.
If the server is in online backup mode, it additionally waits If the server is in recovery when a smart
until online backup mode is no longer active. While backup mode is
active, new connections will still be allowed, but only to superusers
(this exception allows a superuser to connect to terminate
online backup mode). If the server is in recovery when a smart
shutdown is requested, recovery and streaming replication will be shutdown is requested, recovery and streaming replication will be
stopped only after all regular sessions have terminated. stopped only after all regular sessions have terminated.
</para> </para>
@ -1572,8 +1568,6 @@ $ <userinput>cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages</userinp
server processes <systemitem>SIGTERM</systemitem>, which will cause them server processes <systemitem>SIGTERM</systemitem>, which will cause them
to abort their current transactions and exit promptly. It then to abort their current transactions and exit promptly. It then
waits for all server processes to exit and finally shuts down. waits for all server processes to exit and finally shuts down.
If the server is in online backup mode, backup mode will be
terminated, rendering the backup useless.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -385,29 +385,6 @@ typedef union WALInsertLockPadded
char pad[PG_CACHE_LINE_SIZE]; char pad[PG_CACHE_LINE_SIZE];
} WALInsertLockPadded; } WALInsertLockPadded;
/*
* State of an exclusive backup, necessary to control concurrent activities
* across sessions when working on exclusive backups.
*
* EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually
* running, to be more precise pg_start_backup() is not being executed for
* an exclusive backup and there is no exclusive backup in progress.
* EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an
* exclusive backup.
* EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished
* running and an exclusive backup is in progress. pg_stop_backup() is
* needed to finish it.
* EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an
* exclusive backup.
*/
typedef enum ExclusiveBackupState
{
EXCLUSIVE_BACKUP_NONE = 0,
EXCLUSIVE_BACKUP_STARTING,
EXCLUSIVE_BACKUP_IN_PROGRESS,
EXCLUSIVE_BACKUP_STOPPING
} ExclusiveBackupState;
/* /*
* Session status of running backup, used for sanity checks in SQL-callable * Session status of running backup, used for sanity checks in SQL-callable
* functions to start and stop backups. * functions to start and stop backups.
@ -456,15 +433,12 @@ typedef struct XLogCtlInsert
bool fullPageWrites; bool fullPageWrites;
/* /*
* exclusiveBackupState indicates the state of an exclusive backup (see * runningBackups is a counter indicating the number of backups currently in
* comments of ExclusiveBackupState for more details). nonExclusiveBackups * progress. forcePageWrites is set to true when runningBackups is non-zero.
* is a counter indicating the number of streaming base backups currently * lastBackupStart is the latest checkpoint redo location used as a starting
* in progress. forcePageWrites is set to true when either of these is * point for an online backup.
* non-zero. lastBackupStart is the latest checkpoint redo location used
* as a starting point for an online backup.
*/ */
ExclusiveBackupState exclusiveBackupState; int runningBackups;
int nonExclusiveBackups;
XLogRecPtr lastBackupStart; XLogRecPtr lastBackupStart;
/* /*
@ -696,8 +670,7 @@ static void ReadControlFile(void);
static void UpdateControlFile(void); static void UpdateControlFile(void);
static char *str_time(pg_time_t tnow); static char *str_time(pg_time_t tnow);
static void pg_start_backup_callback(int code, Datum arg); static void pg_backup_start_callback(int code, Datum arg);
static void pg_stop_backup_callback(int code, Datum arg);
static int get_sync_bit(int method); static int get_sync_bit(int method);
@ -5314,8 +5287,19 @@ StartupXLOG(void)
missingContrecPtr = endOfRecoveryInfo->missingContrecPtr; missingContrecPtr = endOfRecoveryInfo->missingContrecPtr;
/* /*
* Complain if we did not roll forward far enough to render the backup * When recovering from a backup (we are in recovery, and archive recovery
* dump consistent. Note: it is indeed okay to look at the local variable * was requested), complain if we did not roll forward far enough to reach
* the point where the database is consistent. For regular online
* backup-from-primary, that means reaching the end-of-backup WAL record (at
* which point we reset backupStartPoint to be Invalid), for
* backup-from-replica (which can't inject records into the WAL stream),
* that point is when we reach the minRecoveryPoint in pg_control (which
* we purposfully copy last when backing up from a replica). For pg_rewind
* (which creates a backup_label with a method of "pg_rewind") or
* snapshot-style backups (which don't), backupEndRequired will be set to
* false.
*
* Note: it is indeed okay to look at the local variable
* LocalMinRecoveryPoint here, even though ControlFile->minRecoveryPoint * LocalMinRecoveryPoint here, even though ControlFile->minRecoveryPoint
* might be further ahead --- ControlFile->minRecoveryPoint cannot have * might be further ahead --- ControlFile->minRecoveryPoint cannot have
* been advanced beyond the WAL we processed. * been advanced beyond the WAL we processed.
@ -5326,23 +5310,16 @@ StartupXLOG(void)
{ {
/* /*
* Ran off end of WAL before reaching end-of-backup WAL record, or * Ran off end of WAL before reaching end-of-backup WAL record, or
* minRecoveryPoint. That's usually a bad sign, indicating that you * minRecoveryPoint. That's a bad sign, indicating that you tried to
* tried to recover from an online backup but never called * recover from an online backup but never called pg_backup_stop(),
* pg_stop_backup(), or you didn't archive all the WAL up to that * or you didn't archive all the WAL needed.
* point. However, this also happens in crash recovery, if the system
* crashes while an online backup is in progress. We must not treat
* that as an error, or the database will refuse to start up.
*/ */
if (ArchiveRecoveryRequested || ControlFile->backupEndRequired) if (ArchiveRecoveryRequested || ControlFile->backupEndRequired)
{ {
if (ControlFile->backupEndRequired) if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) || ControlFile->backupEndRequired)
ereport(FATAL, ereport(FATAL,
(errmsg("WAL ends before end of online backup"), (errmsg("WAL ends before end of online backup"),
errhint("All WAL generated while online backup was taken must be available at recovery."))); errhint("All WAL generated while online backup was taken must be available at recovery.")));
else if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint))
ereport(FATAL,
(errmsg("WAL ends before end of online backup"),
errhint("Online backup started with pg_start_backup() must be ended with pg_stop_backup(), and all WAL up to that point must be available at recovery.")));
else else
ereport(FATAL, ereport(FATAL,
(errmsg("WAL ends before consistent recovery point"))); (errmsg("WAL ends before consistent recovery point")));
@ -7036,7 +7013,7 @@ CreateRestartPoint(int flags)
* Ensure minRecoveryPoint is past the checkpoint record. Normally, * Ensure minRecoveryPoint is past the checkpoint record. Normally,
* this will have happened already while writing out dirty buffers, * this will have happened already while writing out dirty buffers,
* but not necessarily - e.g. because no buffers were dirtied. We do * but not necessarily - e.g. because no buffers were dirtied. We do
* this because a non-exclusive base backup uses minRecoveryPoint to * this because a backup performed in recovery uses minRecoveryPoint to
* determine which WAL files must be included in the backup, and the * determine which WAL files must be included in the backup, and the
* file (or files) containing the checkpoint record must be included, * file (or files) containing the checkpoint record must be included,
* at a minimum. Note that for an ordinary restart of recovery there's * at a minimum. Note that for an ordinary restart of recovery there's
@ -7840,7 +7817,7 @@ xlog_redo(XLogReaderState *record)
/* /*
* Update the LSN of the last replayed XLOG_FPW_CHANGE record so that * Update the LSN of the last replayed XLOG_FPW_CHANGE record so that
* do_pg_start_backup() and do_pg_stop_backup() can check whether * do_pg_backup_start() and do_pg_backup_stop() can check whether
* full_page_writes has been disabled during online backup. * full_page_writes has been disabled during online backup.
*/ */
if (!fpw) if (!fpw)
@ -8039,29 +8016,14 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
} }
/* /*
* do_pg_start_backup * do_pg_backup_start is the workhorse of the user-visible pg_backup_start()
* function. It creates the necessary starting checkpoint and constructs the
* backup label and tablespace map.
* *
* Utility function called at the start of an online backup. It creates the * The backup label and tablespace map contents are returned in *labelfile and
* necessary starting checkpoint and constructs the backup label file. * *tblspcmapfile, and the caller is responsible for including them in the
* * backup archive as 'backup_label' and 'tablespace_map'. There can be many
* There are two kind of backups: exclusive and non-exclusive. An exclusive * backups active at the same time.
* backup is started with pg_start_backup(), and there can be only one active
* at a time. The backup and tablespace map files of an exclusive backup are
* written to $PGDATA/backup_label and $PGDATA/tablespace_map, and they are
* removed by pg_stop_backup().
*
* A non-exclusive backup is used for the streaming base backups (see
* src/backend/replication/basebackup.c). The difference to exclusive backups
* is that the backup label and tablespace map files are not written to disk.
* Instead, their would-be contents are returned in *labelfile and *tblspcmapfile,
* and the caller is responsible for including them in the backup archive as
* 'backup_label' and 'tablespace_map'. There can be many non-exclusive backups
* active at the same time, and they don't conflict with an exclusive backup
* either.
*
* labelfile and tblspcmapfile must be passed as NULL when starting an
* exclusive backup, and as initially-empty StringInfos for a non-exclusive
* backup.
* *
* If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs * If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs
* describing the cluster's tablespaces. * describing the cluster's tablespaces.
@ -8073,18 +8035,17 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
* Returns the minimum WAL location that must be present to restore from this * Returns the minimum WAL location that must be present to restore from this
* backup, and the corresponding timeline ID in *starttli_p. * backup, and the corresponding timeline ID in *starttli_p.
* *
* Every successfully started non-exclusive backup must be stopped by calling * Every successfully started backup must be stopped by calling
* do_pg_stop_backup() or do_pg_abort_backup(). * do_pg_backup_stop() or do_pg_abort_backup().
* *
* It is the responsibility of the caller of this function to verify the * It is the responsibility of the caller of this function to verify the
* permissions of the calling user! * permissions of the calling user!
*/ */
XLogRecPtr XLogRecPtr
do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
StringInfo labelfile, List **tablespaces, StringInfo labelfile, List **tablespaces,
StringInfo tblspcmapfile) StringInfo tblspcmapfile)
{ {
bool exclusive = (labelfile == NULL);
bool backup_started_in_recovery = false; bool backup_started_in_recovery = false;
XLogRecPtr checkpointloc; XLogRecPtr checkpointloc;
XLogRecPtr startpoint; XLogRecPtr startpoint;
@ -8093,20 +8054,9 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
char strfbuf[128]; char strfbuf[128];
char xlogfilename[MAXFNAMELEN]; char xlogfilename[MAXFNAMELEN];
XLogSegNo _logSegNo; XLogSegNo _logSegNo;
struct stat stat_buf;
FILE *fp;
backup_started_in_recovery = RecoveryInProgress(); backup_started_in_recovery = RecoveryInProgress();
/*
* Currently only non-exclusive backup can be taken during recovery.
*/
if (backup_started_in_recovery && exclusive)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("recovery is in progress"),
errhint("WAL control functions cannot be executed during recovery.")));
/* /*
* During recovery, we don't need to check WAL level. Because, if WAL * During recovery, we don't need to check WAL level. Because, if WAL
* level is not sufficient, it's impossible to get here during recovery. * level is not sufficient, it's impossible to get here during recovery.
@ -8145,30 +8095,12 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* XLogInsertRecord(). * XLogInsertRecord().
*/ */
WALInsertLockAcquireExclusive(); WALInsertLockAcquireExclusive();
if (exclusive) XLogCtl->Insert.runningBackups++;
{
/*
* At first, mark that we're now starting an exclusive backup, to
* ensure that there are no other sessions currently running
* pg_start_backup() or pg_stop_backup().
*/
if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_NONE)
{
WALInsertLockRelease();
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("a backup is already in progress"),
errhint("Run pg_stop_backup() and try again.")));
}
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING;
}
else
XLogCtl->Insert.nonExclusiveBackups++;
XLogCtl->Insert.forcePageWrites = true; XLogCtl->Insert.forcePageWrites = true;
WALInsertLockRelease(); WALInsertLockRelease();
/* Ensure we release forcePageWrites if fail below */ /* Ensure we release forcePageWrites if fail below */
PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive)); PG_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
{ {
bool gotUniqueStartpoint = false; bool gotUniqueStartpoint = false;
DIR *tblspcdir; DIR *tblspcdir;
@ -8180,7 +8112,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* Force an XLOG file switch before the checkpoint, to ensure that the * Force an XLOG file switch before the checkpoint, to ensure that the
* WAL segment the checkpoint is written to doesn't contain pages with * WAL segment the checkpoint is written to doesn't contain pages with
* old timeline IDs. That would otherwise happen if you called * old timeline IDs. That would otherwise happen if you called
* pg_start_backup() right after restoring from a PITR archive: the * pg_backup_start() right after restoring from a PITR archive: the
* first WAL segment containing the startup checkpoint has pages in * first WAL segment containing the startup checkpoint has pages in
* the beginning with the old timeline ID. That can cause trouble at * the beginning with the old timeline ID. That can cause trouble at
* recovery: we won't have a history file covering the old timeline if * recovery: we won't have a history file covering the old timeline if
@ -8215,7 +8147,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* means that two successive backup runs can have same checkpoint * means that two successive backup runs can have same checkpoint
* positions. * positions.
* *
* Since the fact that we are executing do_pg_start_backup() * Since the fact that we are executing do_pg_backup_start()
* during recovery means that checkpointer is running, we can use * during recovery means that checkpointer is running, we can use
* RequestCheckpoint() to establish a restartpoint. * RequestCheckpoint() to establish a restartpoint.
* *
@ -8416,122 +8348,19 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
LSN_FORMAT_ARGS(startpoint), xlogfilename); LSN_FORMAT_ARGS(startpoint), xlogfilename);
appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n", appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n",
LSN_FORMAT_ARGS(checkpointloc)); LSN_FORMAT_ARGS(checkpointloc));
appendStringInfo(labelfile, "BACKUP METHOD: %s\n", appendStringInfo(labelfile, "BACKUP METHOD: streamed\n");
exclusive ? "pg_start_backup" : "streamed");
appendStringInfo(labelfile, "BACKUP FROM: %s\n", appendStringInfo(labelfile, "BACKUP FROM: %s\n",
backup_started_in_recovery ? "standby" : "primary"); backup_started_in_recovery ? "standby" : "primary");
appendStringInfo(labelfile, "START TIME: %s\n", strfbuf); appendStringInfo(labelfile, "START TIME: %s\n", strfbuf);
appendStringInfo(labelfile, "LABEL: %s\n", backupidstr); appendStringInfo(labelfile, "LABEL: %s\n", backupidstr);
appendStringInfo(labelfile, "START TIMELINE: %u\n", starttli); appendStringInfo(labelfile, "START TIMELINE: %u\n", starttli);
/*
* Okay, write the file, or return its contents to caller.
*/
if (exclusive)
{
/*
* Check for existing backup label --- implies a backup is already
* running. (XXX given that we checked exclusiveBackupState
* above, maybe it would be OK to just unlink any such label
* file?)
*/
if (stat(BACKUP_LABEL_FILE, &stat_buf) != 0)
{
if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
BACKUP_LABEL_FILE)));
}
else
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("a backup is already in progress"),
errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.",
BACKUP_LABEL_FILE)));
fp = AllocateFile(BACKUP_LABEL_FILE, "w");
if (!fp)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create file \"%s\": %m",
BACKUP_LABEL_FILE)));
if (fwrite(labelfile->data, labelfile->len, 1, fp) != 1 ||
fflush(fp) != 0 ||
pg_fsync(fileno(fp)) != 0 ||
ferror(fp) ||
FreeFile(fp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write file \"%s\": %m",
BACKUP_LABEL_FILE)));
/* Allocated locally for exclusive backups, so free separately */
pfree(labelfile->data);
pfree(labelfile);
/* Write backup tablespace_map file. */
if (tblspcmapfile->len > 0)
{
if (stat(TABLESPACE_MAP, &stat_buf) != 0)
{
if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
TABLESPACE_MAP)));
}
else
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("a backup is already in progress"),
errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.",
TABLESPACE_MAP)));
fp = AllocateFile(TABLESPACE_MAP, "w");
if (!fp)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create file \"%s\": %m",
TABLESPACE_MAP)));
if (fwrite(tblspcmapfile->data, tblspcmapfile->len, 1, fp) != 1 ||
fflush(fp) != 0 ||
pg_fsync(fileno(fp)) != 0 ||
ferror(fp) ||
FreeFile(fp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write file \"%s\": %m",
TABLESPACE_MAP)));
}
/* Allocated locally for exclusive backups, so free separately */
pfree(tblspcmapfile->data);
pfree(tblspcmapfile);
}
} }
PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive)); PG_END_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
/* /*
* Mark that start phase has correctly finished for an exclusive backup. * Mark that the start phase has correctly finished for the backup.
* Session-level locks are updated as well to reflect that state.
*
* Note that CHECK_FOR_INTERRUPTS() must not occur while updating backup
* counters and session-level lock. Otherwise they can be updated
* inconsistently, and which might cause do_pg_abort_backup() to fail.
*/ */
if (exclusive) sessionBackupState = SESSION_BACKUP_RUNNING;
{
WALInsertLockAcquireExclusive();
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS;
/* Set session-level lock */
sessionBackupState = SESSION_BACKUP_EXCLUSIVE;
WALInsertLockRelease();
}
else
sessionBackupState = SESSION_BACKUP_NON_EXCLUSIVE;
/* /*
* We're done. As a convenience, return the starting WAL location. * We're done. As a convenience, return the starting WAL location.
@ -8541,51 +8370,23 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
return startpoint; return startpoint;
} }
/* Error cleanup callback for pg_start_backup */ /* Error cleanup callback for pg_backup_start */
static void static void
pg_start_backup_callback(int code, Datum arg) pg_backup_start_callback(int code, Datum arg)
{ {
bool exclusive = DatumGetBool(arg);
/* Update backup counters and forcePageWrites on failure */ /* Update backup counters and forcePageWrites on failure */
WALInsertLockAcquireExclusive(); WALInsertLockAcquireExclusive();
if (exclusive)
{
Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING);
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE;
}
else
{
Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
XLogCtl->Insert.nonExclusiveBackups--;
}
if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && Assert(XLogCtl->Insert.runningBackups > 0);
XLogCtl->Insert.nonExclusiveBackups == 0) XLogCtl->Insert.runningBackups--;
if (XLogCtl->Insert.runningBackups == 0)
{ {
XLogCtl->Insert.forcePageWrites = false; XLogCtl->Insert.forcePageWrites = false;
} }
WALInsertLockRelease(); WALInsertLockRelease();
} }
/*
* Error cleanup callback for pg_stop_backup
*/
static void
pg_stop_backup_callback(int code, Datum arg)
{
bool exclusive = DatumGetBool(arg);
/* Update backup status on failure */
WALInsertLockAcquireExclusive();
if (exclusive)
{
Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING);
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS;
}
WALInsertLockRelease();
}
/* /*
* Utility routine to fetch the session-level status of a backup running. * Utility routine to fetch the session-level status of a backup running.
*/ */
@ -8596,14 +8397,11 @@ get_backup_status(void)
} }
/* /*
* do_pg_stop_backup * do_pg_backup_stop
* *
* Utility function called at the end of an online backup. It cleans up the * Utility function called at the end of an online backup. It cleans up the
* backup state and can optionally wait for WAL segments to be archived. * backup state and can optionally wait for WAL segments to be archived.
* *
* If labelfile is NULL, this stops an exclusive backup. Otherwise this stops
* the non-exclusive backup specified by 'labelfile'.
*
* Returns the last WAL location that must be present to restore from this * Returns the last WAL location that must be present to restore from this
* backup, and the corresponding timeline ID in *stoptli_p. * backup, and the corresponding timeline ID in *stoptli_p.
* *
@ -8611,9 +8409,8 @@ get_backup_status(void)
* permissions of the calling user! * permissions of the calling user!
*/ */
XLogRecPtr XLogRecPtr
do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
{ {
bool exclusive = (labelfile == NULL);
bool backup_started_in_recovery = false; bool backup_started_in_recovery = false;
XLogRecPtr startpoint; XLogRecPtr startpoint;
XLogRecPtr stoppoint; XLogRecPtr stoppoint;
@ -8627,7 +8424,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
char histfilename[MAXFNAMELEN]; char histfilename[MAXFNAMELEN];
char backupfrom[20]; char backupfrom[20];
XLogSegNo _logSegNo; XLogSegNo _logSegNo;
FILE *lfp;
FILE *fp; FILE *fp;
char ch; char ch;
int seconds_before_warning; int seconds_before_warning;
@ -8640,15 +8436,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
backup_started_in_recovery = RecoveryInProgress(); backup_started_in_recovery = RecoveryInProgress();
/*
* Currently only non-exclusive backup can be taken during recovery.
*/
if (backup_started_in_recovery && exclusive)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("recovery is in progress"),
errhint("WAL control functions cannot be executed during recovery.")));
/* /*
* During recovery, we don't need to check WAL level. Because, if WAL * During recovery, we don't need to check WAL level. Because, if WAL
* level is not sufficient, it's impossible to get here during recovery. * level is not sufficient, it's impossible to get here during recovery.
@ -8659,106 +8446,23 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
errmsg("WAL level not sufficient for making an online backup"), errmsg("WAL level not sufficient for making an online backup"),
errhint("wal_level must be set to \"replica\" or \"logical\" at server start."))); errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
if (exclusive)
{
/*
* At first, mark that we're now stopping an exclusive backup, to
* ensure that there are no other sessions currently running
* pg_start_backup() or pg_stop_backup().
*/
WALInsertLockAcquireExclusive();
if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS)
{
WALInsertLockRelease();
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("exclusive backup not in progress")));
}
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING;
WALInsertLockRelease();
/*
* Remove backup_label. In case of failure, the state for an exclusive
* backup is switched back to in-progress.
*/
PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
{
/*
* Read the existing label file into memory.
*/
struct stat statbuf;
int r;
if (stat(BACKUP_LABEL_FILE, &statbuf))
{
/* should not happen per the upper checks */
if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
BACKUP_LABEL_FILE)));
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("a backup is not in progress")));
}
lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
if (!lfp)
{
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
BACKUP_LABEL_FILE)));
}
labelfile = palloc(statbuf.st_size + 1);
r = fread(labelfile, statbuf.st_size, 1, lfp);
labelfile[statbuf.st_size] = '\0';
/*
* Close and remove the backup label file
*/
if (r != 1 || ferror(lfp) || FreeFile(lfp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
BACKUP_LABEL_FILE)));
durable_unlink(BACKUP_LABEL_FILE, ERROR);
/*
* Remove tablespace_map file if present, it is created only if
* there are tablespaces.
*/
durable_unlink(TABLESPACE_MAP, DEBUG1);
}
PG_END_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
}
/* /*
* OK to update backup counters, forcePageWrites and session-level lock. * OK to update backup counters, forcePageWrites, and session-level lock.
* *
* Note that CHECK_FOR_INTERRUPTS() must not occur while updating them. * Note that CHECK_FOR_INTERRUPTS() must not occur while updating them.
* Otherwise they can be updated inconsistently, and which might cause * Otherwise they can be updated inconsistently, and which might cause
* do_pg_abort_backup() to fail. * do_pg_abort_backup() to fail.
*/ */
WALInsertLockAcquireExclusive(); WALInsertLockAcquireExclusive();
if (exclusive)
{
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE;
}
else
{
/*
* The user-visible pg_start/stop_backup() functions that operate on
* exclusive backups can be called at any time, but for non-exclusive
* backups, it is expected that each do_pg_start_backup() call is
* matched by exactly one do_pg_stop_backup() call.
*/
Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
XLogCtl->Insert.nonExclusiveBackups--;
}
if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && /*
XLogCtl->Insert.nonExclusiveBackups == 0) * It is expected that each do_pg_backup_start() call is matched by exactly
* one do_pg_backup_stop() call.
*/
Assert(XLogCtl->Insert.runningBackups > 0);
XLogCtl->Insert.runningBackups--;
if (XLogCtl->Insert.runningBackups == 0)
{ {
XLogCtl->Insert.forcePageWrites = false; XLogCtl->Insert.forcePageWrites = false;
} }
@ -9016,17 +8720,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
/* /*
* do_pg_abort_backup: abort a running backup * do_pg_abort_backup: abort a running backup
* *
* This does just the most basic steps of do_pg_stop_backup(), by taking the * This does just the most basic steps of do_pg_backup_stop(), by taking the
* system out of backup mode, thus making it a lot more safe to call from * system out of backup mode, thus making it a lot more safe to call from
* an error handler. * an error handler.
* *
* The caller can pass 'arg' as 'true' or 'false' to control whether a warning * The caller can pass 'arg' as 'true' or 'false' to control whether a warning
* is emitted. * is emitted.
* *
* NB: This is only for aborting a non-exclusive backup that doesn't write
* backup_label. A backup started with pg_start_backup() needs to be finished
* with pg_stop_backup().
*
* NB: This gets used as a before_shmem_exit handler, hence the odd-looking * NB: This gets used as a before_shmem_exit handler, hence the odd-looking
* signature. * signature.
*/ */
@ -9036,18 +8736,16 @@ do_pg_abort_backup(int code, Datum arg)
bool emit_warning = DatumGetBool(arg); bool emit_warning = DatumGetBool(arg);
/* /*
* Quick exit if session is not keeping around a non-exclusive backup * Quick exit if session does not have a running backup.
* already started.
*/ */
if (sessionBackupState != SESSION_BACKUP_NON_EXCLUSIVE) if (sessionBackupState != SESSION_BACKUP_RUNNING)
return; return;
WALInsertLockAcquireExclusive(); WALInsertLockAcquireExclusive();
Assert(XLogCtl->Insert.nonExclusiveBackups > 0); Assert(XLogCtl->Insert.runningBackups > 0);
XLogCtl->Insert.nonExclusiveBackups--; XLogCtl->Insert.runningBackups--;
if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && if (XLogCtl->Insert.runningBackups == 0)
XLogCtl->Insert.nonExclusiveBackups == 0)
{ {
XLogCtl->Insert.forcePageWrites = false; XLogCtl->Insert.forcePageWrites = false;
} }
@ -9055,7 +8753,7 @@ do_pg_abort_backup(int code, Datum arg)
if (emit_warning) if (emit_warning)
ereport(WARNING, ereport(WARNING,
(errmsg("aborting backup due to backend exiting before pg_stop_backup was called"))); (errmsg("aborting backup due to backend exiting before pg_backup_stop was called")));
} }
/* /*
@ -9115,87 +8813,6 @@ GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli)
LWLockRelease(ControlFileLock); LWLockRelease(ControlFileLock);
} }
/*
* BackupInProgress: check if online backup mode is active
*
* This is done by checking for existence of the "backup_label" file.
*/
bool
BackupInProgress(void)
{
struct stat stat_buf;
return (stat(BACKUP_LABEL_FILE, &stat_buf) == 0);
}
/*
* CancelBackup: rename the "backup_label" and "tablespace_map"
* files to cancel backup mode
*
* If the "backup_label" file exists, it will be renamed to "backup_label.old".
* Similarly, if the "tablespace_map" file exists, it will be renamed to
* "tablespace_map.old".
*
* Note that this will render an online backup in progress
* useless. To correctly finish an online backup, pg_stop_backup must be
* called.
*/
void
CancelBackup(void)
{
struct stat stat_buf;
/* if the backup_label file is not there, return */
if (stat(BACKUP_LABEL_FILE, &stat_buf) < 0)
return;
/* remove leftover file from previously canceled backup if it exists */
unlink(BACKUP_LABEL_OLD);
if (durable_rename(BACKUP_LABEL_FILE, BACKUP_LABEL_OLD, DEBUG1) != 0)
{
ereport(WARNING,
(errcode_for_file_access(),
errmsg("online backup mode was not canceled"),
errdetail("File \"%s\" could not be renamed to \"%s\": %m.",
BACKUP_LABEL_FILE, BACKUP_LABEL_OLD)));
return;
}
/* if the tablespace_map file is not there, return */
if (stat(TABLESPACE_MAP, &stat_buf) < 0)
{
ereport(LOG,
(errmsg("online backup mode canceled"),
errdetail("File \"%s\" was renamed to \"%s\".",
BACKUP_LABEL_FILE, BACKUP_LABEL_OLD)));
return;
}
/* remove leftover file from previously canceled backup if it exists */
unlink(TABLESPACE_MAP_OLD);
if (durable_rename(TABLESPACE_MAP, TABLESPACE_MAP_OLD, DEBUG1) == 0)
{
ereport(LOG,
(errmsg("online backup mode canceled"),
errdetail("Files \"%s\" and \"%s\" were renamed to "
"\"%s\" and \"%s\", respectively.",
BACKUP_LABEL_FILE, TABLESPACE_MAP,
BACKUP_LABEL_OLD, TABLESPACE_MAP_OLD)));
}
else
{
ereport(WARNING,
(errcode_for_file_access(),
errmsg("online backup mode canceled"),
errdetail("File \"%s\" was renamed to \"%s\", but "
"file \"%s\" could not be renamed to \"%s\": %m.",
BACKUP_LABEL_FILE, BACKUP_LABEL_OLD,
TABLESPACE_MAP, TABLESPACE_MAP_OLD)));
}
}
/* Thin wrapper around ShutdownWalRcv(). */ /* Thin wrapper around ShutdownWalRcv(). */
void void
XLogShutdownWalRcv(void) XLogShutdownWalRcv(void)

View File

@ -39,13 +39,13 @@
#include "utils/tuplestore.h" #include "utils/tuplestore.h"
/* /*
* Store label file and tablespace map during non-exclusive backups. * Store label file and tablespace map during backups.
*/ */
static StringInfo label_file; static StringInfo label_file;
static StringInfo tblspc_map_file; static StringInfo tblspc_map_file;
/* /*
* pg_start_backup: set up for taking an on-line backup dump * pg_backup_start: set up for taking an on-line backup dump
* *
* Essentially what this does is to create a backup label file in $PGDATA, * Essentially what this does is to create a backup label file in $PGDATA,
* where it will be archived as part of the backup dump. The label file * where it will be archived as part of the backup dump. The label file
@ -57,105 +57,44 @@ static StringInfo tblspc_map_file;
* GRANT system. * GRANT system.
*/ */
Datum Datum
pg_start_backup(PG_FUNCTION_ARGS) pg_backup_start(PG_FUNCTION_ARGS)
{ {
text *backupid = PG_GETARG_TEXT_PP(0); text *backupid = PG_GETARG_TEXT_PP(0);
bool fast = PG_GETARG_BOOL(1); bool fast = PG_GETARG_BOOL(1);
bool exclusive = PG_GETARG_BOOL(2);
char *backupidstr; char *backupidstr;
XLogRecPtr startpoint; XLogRecPtr startpoint;
SessionBackupState status = get_backup_status(); SessionBackupState status = get_backup_status();
MemoryContext oldcontext;
backupidstr = text_to_cstring(backupid); backupidstr = text_to_cstring(backupid);
if (status == SESSION_BACKUP_NON_EXCLUSIVE) if (status == SESSION_BACKUP_RUNNING)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("a backup is already in progress in this session"))); errmsg("a backup is already in progress in this session")));
if (exclusive) /*
{ * Label file and tablespace map file need to be long-lived, since
startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL, * they are read in pg_backup_stop.
NULL, NULL); */
} oldcontext = MemoryContextSwitchTo(TopMemoryContext);
else label_file = makeStringInfo();
{ tblspc_map_file = makeStringInfo();
MemoryContext oldcontext; MemoryContextSwitchTo(oldcontext);
/* register_persistent_abort_backup_handler();
* Label file and tablespace map file need to be long-lived, since
* they are read in pg_stop_backup.
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
label_file = makeStringInfo();
tblspc_map_file = makeStringInfo();
MemoryContextSwitchTo(oldcontext);
register_persistent_abort_backup_handler(); startpoint = do_pg_backup_start(backupidstr, fast, NULL, label_file,
NULL, tblspc_map_file);
startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file,
NULL, tblspc_map_file);
}
PG_RETURN_LSN(startpoint); PG_RETURN_LSN(startpoint);
} }
/*
* pg_stop_backup: finish taking an on-line backup dump
*
* We write an end-of-backup WAL record, and remove the backup label file
* created by pg_start_backup, creating a backup history file in pg_wal
* instead (whence it will immediately be archived). The backup history file
* contains the same info found in the label file, plus the backup-end time
* and WAL location. Before 9.0, the backup-end time was read from the backup
* history file at the beginning of archive recovery, but we now use the WAL
* record for that and the file is for informational and debug purposes only.
*
* Note: different from CancelBackup which just cancels online backup mode.
*
* Note: this version is only called to stop an exclusive backup. The function
* pg_stop_backup_v2 (overloaded as pg_stop_backup in SQL) is called to
* stop non-exclusive backups.
*
* Permission checking for this function is managed through the normal
* GRANT system.
*/
Datum
pg_stop_backup(PG_FUNCTION_ARGS)
{
XLogRecPtr stoppoint;
SessionBackupState status = get_backup_status();
if (status == SESSION_BACKUP_NON_EXCLUSIVE)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("non-exclusive backup in progress"),
errhint("Did you mean to use pg_stop_backup('f')?")));
/*
* Exclusive backups were typically started in a different connection, so
* don't try to verify that status of backup is set to
* SESSION_BACKUP_EXCLUSIVE in this function. Actual verification that an
* exclusive backup is in fact running is handled inside
* do_pg_stop_backup.
*/
stoppoint = do_pg_stop_backup(NULL, true, NULL);
PG_RETURN_LSN(stoppoint);
}
/* /*
* pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup. * pg_backup_stop: finish taking an on-line backup.
* *
* Works the same as pg_stop_backup, except for non-exclusive backups it returns * The first parameter (variable 'waitforarchive'), which is optional,
* the backup label and tablespace map files as text fields in as part of the
* resultset.
*
* The first parameter (variable 'exclusive') allows the user to tell us if
* this is an exclusive or a non-exclusive backup.
*
* The second parameter (variable 'waitforarchive'), which is optional,
* allows the user to choose if they want to wait for the WAL to be archived * allows the user to choose if they want to wait for the WAL to be archived
* or if we should just return as soon as the WAL record is written. * or if we should just return as soon as the WAL record is written.
* *
@ -163,15 +102,14 @@ pg_stop_backup(PG_FUNCTION_ARGS)
* GRANT system. * GRANT system.
*/ */
Datum Datum
pg_stop_backup_v2(PG_FUNCTION_ARGS) pg_backup_stop(PG_FUNCTION_ARGS)
{ {
#define PG_STOP_BACKUP_V2_COLS 3 #define PG_STOP_BACKUP_V2_COLS 3
TupleDesc tupdesc; TupleDesc tupdesc;
Datum values[PG_STOP_BACKUP_V2_COLS]; Datum values[PG_STOP_BACKUP_V2_COLS];
bool nulls[PG_STOP_BACKUP_V2_COLS]; bool nulls[PG_STOP_BACKUP_V2_COLS];
bool exclusive = PG_GETARG_BOOL(0); bool waitforarchive = PG_GETARG_BOOL(0);
bool waitforarchive = PG_GETARG_BOOL(1);
XLogRecPtr stoppoint; XLogRecPtr stoppoint;
SessionBackupState status = get_backup_status(); SessionBackupState status = get_backup_status();
@ -182,51 +120,29 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, 0, sizeof(nulls)); MemSet(nulls, 0, sizeof(nulls));
if (exclusive) if (status != SESSION_BACKUP_RUNNING)
{ ereport(ERROR,
if (status == SESSION_BACKUP_NON_EXCLUSIVE) (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
ereport(ERROR, errmsg("backup is not in progress"),
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errhint("Did you call pg_backup_start()?")));
errmsg("non-exclusive backup in progress"),
errhint("Did you mean to use pg_stop_backup('f')?")));
/* /*
* Stop the exclusive backup, and since we're in an exclusive backup * Stop the backup. Return a copy of the backup label and tablespace map so
* return NULL for both backup_label and tablespace_map. * they can be written to disk by the caller.
*/ */
stoppoint = do_pg_stop_backup(NULL, waitforarchive, NULL); stoppoint = do_pg_backup_stop(label_file->data, waitforarchive, NULL);
nulls[1] = true;
nulls[2] = true;
}
else
{
if (status != SESSION_BACKUP_NON_EXCLUSIVE)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("non-exclusive backup is not in progress"),
errhint("Did you mean to use pg_stop_backup('t')?")));
/*
* Stop the non-exclusive backup. Return a copy of the backup label
* and tablespace map so they can be written to disk by the caller.
*/
stoppoint = do_pg_stop_backup(label_file->data, waitforarchive, NULL);
values[1] = CStringGetTextDatum(label_file->data);
values[2] = CStringGetTextDatum(tblspc_map_file->data);
/* Free structures allocated in TopMemoryContext */
pfree(label_file->data);
pfree(label_file);
label_file = NULL;
pfree(tblspc_map_file->data);
pfree(tblspc_map_file);
tblspc_map_file = NULL;
}
/* Stoppoint is included on both exclusive and nonexclusive backups */
values[0] = LSNGetDatum(stoppoint); values[0] = LSNGetDatum(stoppoint);
values[1] = CStringGetTextDatum(label_file->data);
values[2] = CStringGetTextDatum(tblspc_map_file->data);
/* Free structures allocated in TopMemoryContext */
pfree(label_file->data);
pfree(label_file);
label_file = NULL;
pfree(tblspc_map_file->data);
pfree(tblspc_map_file);
tblspc_map_file = NULL;
/* Returns the record as Datum */ /* Returns the record as Datum */
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
@ -298,7 +214,7 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
} }
/* /*
* Report the current WAL write location (same format as pg_start_backup etc) * Report the current WAL write location (same format as pg_backup_start etc)
* *
* This is useful for determining how much of WAL is visible to an external * This is useful for determining how much of WAL is visible to an external
* archiving process. Note that the data before this point is written out * archiving process. Note that the data before this point is written out
@ -321,7 +237,7 @@ pg_current_wal_lsn(PG_FUNCTION_ARGS)
} }
/* /*
* Report the current WAL insert location (same format as pg_start_backup etc) * Report the current WAL insert location (same format as pg_backup_start etc)
* *
* This function is mostly for debugging purposes. * This function is mostly for debugging purposes.
*/ */
@ -342,7 +258,7 @@ pg_current_wal_insert_lsn(PG_FUNCTION_ARGS)
} }
/* /*
* Report the current WAL flush location (same format as pg_start_backup etc) * Report the current WAL flush location (same format as pg_backup_start etc)
* *
* This function is mostly for debugging purposes. * This function is mostly for debugging purposes.
*/ */
@ -363,7 +279,7 @@ pg_current_wal_flush_lsn(PG_FUNCTION_ARGS)
} }
/* /*
* Report the last WAL receive location (same format as pg_start_backup etc) * Report the last WAL receive location (same format as pg_backup_start etc)
* *
* This is useful for determining how much of WAL is guaranteed to be received * This is useful for determining how much of WAL is guaranteed to be received
* and synced to disk by walreceiver. * and synced to disk by walreceiver.
@ -382,7 +298,7 @@ pg_last_wal_receive_lsn(PG_FUNCTION_ARGS)
} }
/* /*
* Report the last WAL replay location (same format as pg_start_backup etc) * Report the last WAL replay location (same format as pg_backup_start etc)
* *
* This is useful for determining how much of WAL is visible to read-only * This is useful for determining how much of WAL is visible to read-only
* connections during recovery. * connections during recovery.
@ -402,7 +318,7 @@ pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
/* /*
* Compute an xlog file name and decimal byte offset given a WAL location, * Compute an xlog file name and decimal byte offset given a WAL location,
* such as is returned by pg_stop_backup() or pg_switch_wal(). * such as is returned by pg_backup_stop() or pg_switch_wal().
* *
* Note that a location exactly at a segment boundary is taken to be in * Note that a location exactly at a segment boundary is taken to be in
* the previous segment. This is usually the right thing, since the * the previous segment. This is usually the right thing, since the
@ -470,7 +386,7 @@ pg_walfile_name_offset(PG_FUNCTION_ARGS)
/* /*
* Compute an xlog file name given a WAL location, * Compute an xlog file name given a WAL location,
* such as is returned by pg_stop_backup() or pg_switch_wal(). * such as is returned by pg_backup_stop() or pg_switch_wal().
*/ */
Datum Datum
pg_walfile_name(PG_FUNCTION_ARGS) pg_walfile_name(PG_FUNCTION_ARGS)
@ -645,81 +561,6 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(result); PG_RETURN_NUMERIC(result);
} }
/*
* Returns bool with current on-line backup mode, a global state.
*/
Datum
pg_is_in_backup(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(BackupInProgress());
}
/*
* Returns start time of an online exclusive backup.
*
* When there's no exclusive backup in progress, the function
* returns NULL.
*/
Datum
pg_backup_start_time(PG_FUNCTION_ARGS)
{
Datum xtime;
FILE *lfp;
char fline[MAXPGPATH];
char backup_start_time[30];
/*
* See if label file is present
*/
lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
if (lfp == NULL)
{
if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
BACKUP_LABEL_FILE)));
PG_RETURN_NULL();
}
/*
* Parse the file to find the START TIME line.
*/
backup_start_time[0] = '\0';
while (fgets(fline, sizeof(fline), lfp) != NULL)
{
if (sscanf(fline, "START TIME: %25[^\n]\n", backup_start_time) == 1)
break;
}
/* Check for a read error. */
if (ferror(lfp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE)));
/* Close the backup label file. */
if (FreeFile(lfp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close file \"%s\": %m", BACKUP_LABEL_FILE)));
if (strlen(backup_start_time) == 0)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
/*
* Convert the time string read from file to TimestampTz form.
*/
xtime = DirectFunctionCall3(timestamptz_in,
CStringGetDatum(backup_start_time),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(-1));
PG_RETURN_DATUM(xtime);
}
/* /*
* Promotes a standby server. * Promotes a standby server.
* *

View File

@ -1183,9 +1183,14 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
*backupLabelTLI = tli_from_walseg; *backupLabelTLI = tli_from_walseg;
/* /*
* BACKUP METHOD and BACKUP FROM lines are new in 9.2. We can't restore * BACKUP METHOD lets us know if this was a typical backup ("streamed",
* from an older backup anyway, but since the information on it is not * which could mean either pg_basebackup or the pg_backup_start/stop
* strictly required, don't error out if it's missing for some reason. * method was used) or if this label came from somewhere else (the only
* other option today being from pg_rewind). If this was a streamed
* backup then we know that we need to play through until we get to the
* end of the WAL which was generated during the backup (at which point
* we will have reached consistency and backupEndRequired will be reset
* to be false).
*/ */
if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1) if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1)
{ {
@ -1193,6 +1198,11 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
*backupEndRequired = true; *backupEndRequired = true;
} }
/*
* BACKUP FROM lets us know if this was from a primary or a standby. If
* it was from a standby, we'll double-check that the control file state
* matches that of a standby.
*/
if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1) if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1)
{ {
if (strcmp(backupfrom, "standby") == 0) if (strcmp(backupfrom, "standby") == 0)
@ -1970,7 +1980,7 @@ xlogrecovery_redo(XLogReaderState *record, TimeLineID replayTLI)
{ {
/* /*
* We have reached the end of base backup, the point where * We have reached the end of base backup, the point where
* pg_stop_backup() was done. The data on disk is now consistent * pg_backup_stop() was done. The data on disk is now consistent
* (assuming we have also reached minRecoveryPoint). Set * (assuming we have also reached minRecoveryPoint). Set
* backupEndPoint to the current LSN, so that the next call to * backupEndPoint to the current LSN, so that the next call to
* CheckRecoveryConsistency() will notice it and do the * CheckRecoveryConsistency() will notice it and do the
@ -2033,7 +2043,7 @@ CheckRecoveryConsistency(void)
/* /*
* Have we passed our safe starting point? Note that minRecoveryPoint is * Have we passed our safe starting point? Note that minRecoveryPoint is
* known to be incorrectly set if ControlFile->backupEndRequired, until * known to be incorrectly set if recovering from a backup, until
* the XLOG_BACKUP_END arrives to advise us of the correct * the XLOG_BACKUP_END arrives to advise us of the correct
* minRecoveryPoint. All we know prior to that is that we're not * minRecoveryPoint. All we know prior to that is that we're not
* consistent yet. * consistent yet.

View File

@ -377,14 +377,14 @@ BEGIN ATOMIC
END; END;
CREATE OR REPLACE FUNCTION CREATE OR REPLACE FUNCTION
pg_start_backup(label text, fast boolean DEFAULT false, exclusive boolean DEFAULT true) pg_backup_start(label text, fast boolean DEFAULT false)
RETURNS pg_lsn STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup' RETURNS pg_lsn STRICT VOLATILE LANGUAGE internal AS 'pg_backup_start'
PARALLEL RESTRICTED; PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION pg_stop_backup ( CREATE OR REPLACE FUNCTION pg_backup_stop (
exclusive boolean, wait_for_archive boolean DEFAULT true, wait_for_archive boolean DEFAULT true, OUT lsn pg_lsn,
OUT lsn pg_lsn, OUT labelfile text, OUT spcmapfile text) OUT labelfile text, OUT spcmapfile text)
RETURNS record STRICT VOLATILE LANGUAGE internal as 'pg_stop_backup_v2' RETURNS record STRICT VOLATILE LANGUAGE internal as 'pg_backup_stop'
PARALLEL RESTRICTED; PARALLEL RESTRICTED;
CREATE OR REPLACE FUNCTION CREATE OR REPLACE FUNCTION
@ -603,11 +603,9 @@ AS 'unicode_is_normalized';
-- available to superuser / cluster owner, if they choose. -- available to superuser / cluster owner, if they choose.
-- --
REVOKE EXECUTE ON FUNCTION pg_start_backup(text, boolean, boolean) FROM public; REVOKE EXECUTE ON FUNCTION pg_backup_start(text, boolean) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stop_backup() FROM public; REVOKE EXECUTE ON FUNCTION pg_backup_stop(boolean) FROM public;
REVOKE EXECUTE ON FUNCTION pg_stop_backup(boolean, boolean) FROM public;
REVOKE EXECUTE ON FUNCTION pg_create_restore_point(text) FROM public; REVOKE EXECUTE ON FUNCTION pg_create_restore_point(text) FROM public;

View File

@ -345,14 +345,7 @@ static PMState pmState = PM_INIT;
* connsAllowed is a sub-state indicator showing the active restriction. * connsAllowed is a sub-state indicator showing the active restriction.
* It is of no interest unless pmState is PM_RUN or PM_HOT_STANDBY. * It is of no interest unless pmState is PM_RUN or PM_HOT_STANDBY.
*/ */
typedef enum static bool connsAllowed = true;
{
ALLOW_ALL_CONNS, /* normal not-shutting-down state */
ALLOW_SUPERUSER_CONNS, /* only superusers can connect */
ALLOW_NO_CONNS /* no new connections allowed, period */
} ConnsAllowedState;
static ConnsAllowedState connsAllowed = ALLOW_ALL_CONNS;
/* Start time of SIGKILL timeout during immediate shutdown or child crash */ /* Start time of SIGKILL timeout during immediate shutdown or child crash */
/* Zero means timeout is not running */ /* Zero means timeout is not running */
@ -2409,9 +2402,6 @@ retry1:
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
errmsg("sorry, too many clients already"))); errmsg("sorry, too many clients already")));
break; break;
case CAC_SUPERUSER:
/* OK for now, will check in InitPostgres */
break;
case CAC_OK: case CAC_OK:
break; break;
} }
@ -2546,19 +2536,10 @@ canAcceptConnections(int backend_type)
/* /*
* "Smart shutdown" restrictions are applied only to normal connections, * "Smart shutdown" restrictions are applied only to normal connections,
* not to autovac workers or bgworkers. When only superusers can connect, * not to autovac workers or bgworkers.
* we return CAC_SUPERUSER to indicate that superuserness must be checked
* later. Note that neither CAC_OK nor CAC_SUPERUSER can safely be
* returned until we have checked for too many children.
*/ */
if (connsAllowed != ALLOW_ALL_CONNS && if (!connsAllowed && backend_type == BACKEND_TYPE_NORMAL)
backend_type == BACKEND_TYPE_NORMAL) return CAC_SHUTDOWN; /* shutdown is pending */
{
if (connsAllowed == ALLOW_SUPERUSER_CONNS)
result = CAC_SUPERUSER; /* allow superusers only */
else
return CAC_SHUTDOWN; /* shutdown is pending */
}
/* /*
* Don't start too many children. * Don't start too many children.
@ -2877,17 +2858,12 @@ pmdie(SIGNAL_ARGS)
#endif #endif
/* /*
* If we reached normal running, we have to wait for any online * If we reached normal running, we go straight to waiting for
* backup mode to end; otherwise go straight to waiting for client * client backends to exit. If already in PM_STOP_BACKENDS or
* backends to exit. (The difference is that in the former state,
* we'll still let in new superuser clients, so that somebody can
* end the online backup mode.) If already in PM_STOP_BACKENDS or
* a later state, do not change it. * a later state, do not change it.
*/ */
if (pmState == PM_RUN) if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
connsAllowed = ALLOW_SUPERUSER_CONNS; connsAllowed = false;
else if (pmState == PM_HOT_STANDBY)
connsAllowed = ALLOW_NO_CONNS;
else if (pmState == PM_STARTUP || pmState == PM_RECOVERY) else if (pmState == PM_STARTUP || pmState == PM_RECOVERY)
{ {
/* There should be no clients, so proceed to stop children */ /* There should be no clients, so proceed to stop children */
@ -3099,7 +3075,7 @@ reaper(SIGNAL_ARGS)
AbortStartTime = 0; AbortStartTime = 0;
ReachedNormalRunning = true; ReachedNormalRunning = true;
pmState = PM_RUN; pmState = PM_RUN;
connsAllowed = ALLOW_ALL_CONNS; connsAllowed = true;
/* /*
* Crank up the background tasks, if we didn't do that already * Crank up the background tasks, if we didn't do that already
@ -3842,21 +3818,11 @@ PostmasterStateMachine(void)
/* If we're doing a smart shutdown, try to advance that state. */ /* If we're doing a smart shutdown, try to advance that state. */
if (pmState == PM_RUN || pmState == PM_HOT_STANDBY) if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
{ {
if (connsAllowed == ALLOW_SUPERUSER_CONNS) if (!connsAllowed)
{ {
/* /*
* ALLOW_SUPERUSER_CONNS state ends as soon as online backup mode * This state ends when we have no normal client backends running.
* is not active. * Then we're ready to stop other children.
*/
if (!BackupInProgress())
connsAllowed = ALLOW_NO_CONNS;
}
if (connsAllowed == ALLOW_NO_CONNS)
{
/*
* ALLOW_NO_CONNS state ends when we have no normal client
* backends running. Then we're ready to stop other children.
*/ */
if (CountChildren(BACKEND_TYPE_NORMAL) == 0) if (CountChildren(BACKEND_TYPE_NORMAL) == 0)
pmState = PM_STOP_BACKENDS; pmState = PM_STOP_BACKENDS;
@ -4044,18 +4010,6 @@ PostmasterStateMachine(void)
} }
else else
{ {
/*
* Terminate exclusive backup mode to avoid recovery after a clean
* fast shutdown. Since an exclusive backup can only be taken
* during normal running (and not, for example, while running
* under Hot Standby) it only makes sense to do this if we reached
* normal running. If we're still in recovery, the backup file is
* one we're recovering *from*, and we must keep it around so that
* recovery restarts from the right place.
*/
if (ReachedNormalRunning)
CancelBackup();
/* /*
* Normal exit from the postmaster is here. We don't need to log * Normal exit from the postmaster is here. We don't need to log
* anything here, since the UnlinkLockFiles proc_exit callback * anything here, since the UnlinkLockFiles proc_exit callback
@ -4277,8 +4231,7 @@ BackendStartup(Port *port)
/* Pass down canAcceptConnections state */ /* Pass down canAcceptConnections state */
port->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL); port->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
bn->dead_end = (port->canAcceptConnections != CAC_OK && bn->dead_end = (port->canAcceptConnections != CAC_OK);
port->canAcceptConnections != CAC_SUPERUSER);
/* /*
* Unless it's a dead_end child, assign it a child slot number * Unless it's a dead_end child, assign it a child slot number
@ -5287,7 +5240,7 @@ sigusr1_handler(SIGNAL_ARGS)
#endif #endif
pmState = PM_HOT_STANDBY; pmState = PM_HOT_STANDBY;
connsAllowed = ALLOW_ALL_CONNS; connsAllowed = true;
/* Some workers may be scheduled to start now */ /* Some workers may be scheduled to start now */
StartWorkerNeeded = true; StartWorkerNeeded = true;

View File

@ -184,10 +184,8 @@ static const struct exclude_list_item excludeFiles[] =
{RELCACHE_INIT_FILENAME, true}, {RELCACHE_INIT_FILENAME, true},
/* /*
* If there's a backup_label or tablespace_map file, it belongs to a * backup_label and tablespace_map should not exist in in a running cluster
* backup started by the user with pg_start_backup(). It is *not* correct * capable of doing an online backup, but exclude them just in case.
* for this backup. Our backup_label/tablespace_map is injected into the
* tar separately.
*/ */
{BACKUP_LABEL_FILE, false}, {BACKUP_LABEL_FILE, false},
{TABLESPACE_MAP, false}, {TABLESPACE_MAP, false},
@ -264,16 +262,16 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
total_checksum_failures = 0; total_checksum_failures = 0;
basebackup_progress_wait_checkpoint(); basebackup_progress_wait_checkpoint();
state.startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, state.startptr = do_pg_backup_start(opt->label, opt->fastcheckpoint,
&state.starttli, &state.starttli,
labelfile, &state.tablespaces, labelfile, &state.tablespaces,
tblspc_map_file); tblspc_map_file);
/* /*
* Once do_pg_start_backup has been called, ensure that any failure causes * Once do_pg_backup_start has been called, ensure that any failure causes
* us to abort the backup so we don't "leak" a backup counter. For this * us to abort the backup so we don't "leak" a backup counter. For this
* reason, *all* functionality between do_pg_start_backup() and the end of * reason, *all* functionality between do_pg_backup_start() and the end of
* do_pg_stop_backup() should be inside the error cleanup block! * do_pg_backup_stop() should be inside the error cleanup block!
*/ */
PG_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false)); PG_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));
@ -394,7 +392,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
} }
basebackup_progress_wait_wal_archive(&state); basebackup_progress_wait_wal_archive(&state);
endptr = do_pg_stop_backup(labelfile->data, !opt->nowait, &endtli); endptr = do_pg_backup_stop(labelfile->data, !opt->nowait, &endtli);
} }
PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false)); PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));
@ -961,7 +959,7 @@ parse_basebackup_options(List *options, basebackup_options *opt)
/* /*
* SendBaseBackup() - send a complete base backup. * SendBaseBackup() - send a complete base backup.
* *
* The function will put the system into backup mode like pg_start_backup() * The function will put the system into backup mode like pg_backup_start()
* does, so that the backup is consistent even though we read directly from * does, so that the backup is consistent even though we read directly from
* the filesystem, bypassing the buffer cache. * the filesystem, bypassing the buffer cache.
*/ */
@ -1204,7 +1202,7 @@ sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
* error in that case. The error handler further up will call * error in that case. The error handler further up will call
* do_pg_abort_backup() for us. Also check that if the backup was * do_pg_abort_backup() for us. Also check that if the backup was
* started while still in recovery, the server wasn't promoted. * started while still in recovery, the server wasn't promoted.
* do_pg_stop_backup() will check that too, but it's better to stop * do_pg_backup_stop() will check that too, but it's better to stop
* the backup early than continue to the end and fail there. * the backup early than continue to the end and fail there.
*/ */
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();

View File

@ -871,24 +871,6 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
am_superuser = superuser(); am_superuser = superuser();
} }
/*
* If we're trying to shut down, only superusers can connect, and new
* replication connections are not allowed.
*/
if ((!am_superuser || am_walsender) &&
MyProcPort != NULL &&
MyProcPort->canAcceptConnections == CAC_SUPERUSER)
{
if (am_walsender)
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("new replication connections are not allowed during database shutdown")));
else
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to connect during database shutdown")));
}
/* /*
* Binary upgrades only allowed super-user connections * Binary upgrades only allowed super-user connections
*/ */

View File

@ -247,6 +247,10 @@ isnt(slurp_file("$tempdir/backup/backup_label"),
'DONOTCOPY', 'existing backup_label not copied'); 'DONOTCOPY', 'existing backup_label not copied');
rmtree("$tempdir/backup"); rmtree("$tempdir/backup");
# Now delete the bogus backup_label file since it will interfere with startup
unlink("$pgdata/backup_label")
or BAIL_OUT("unable to unlink $pgdata/backup_label");
$node->command_ok( $node->command_ok(
[ [
@pg_basebackup_defs, '-D', @pg_basebackup_defs, '-D',

View File

@ -1025,7 +1025,6 @@ static void
do_stop(void) do_stop(void)
{ {
pgpid_t pid; pgpid_t pid;
struct stat statbuf;
pid = get_pgpid(false); pid = get_pgpid(false);
@ -1058,20 +1057,6 @@ do_stop(void)
} }
else else
{ {
/*
* If backup_label exists, an online backup is running. Warn the user
* that smart shutdown will wait for it to finish. However, if the
* server is in archive recovery, we're recovering from an online
* backup instead of performing one.
*/
if (shutdown_mode == SMART_MODE &&
stat(backup_file, &statbuf) == 0 &&
get_control_dbstate() != DB_IN_ARCHIVE_RECOVERY)
{
print_msg(_("WARNING: online backup mode is active\n"
"Shutdown will not complete until pg_stop_backup() is called.\n\n"));
}
print_msg(_("waiting for server to shut down...")); print_msg(_("waiting for server to shut down..."));
if (!wait_for_postmaster_stop()) if (!wait_for_postmaster_stop())
@ -1099,7 +1084,6 @@ static void
do_restart(void) do_restart(void)
{ {
pgpid_t pid; pgpid_t pid;
struct stat statbuf;
pid = get_pgpid(false); pid = get_pgpid(false);
@ -1134,20 +1118,6 @@ do_restart(void)
exit(1); exit(1);
} }
/*
* If backup_label exists, an online backup is running. Warn the user
* that smart shutdown will wait for it to finish. However, if the
* server is in archive recovery, we're recovering from an online
* backup instead of performing one.
*/
if (shutdown_mode == SMART_MODE &&
stat(backup_file, &statbuf) == 0 &&
get_control_dbstate() != DB_IN_ARCHIVE_RECOVERY)
{
print_msg(_("WARNING: online backup mode is active\n"
"Shutdown will not complete until pg_stop_backup() is called.\n\n"));
}
print_msg(_("waiting for server to shut down...")); print_msg(_("waiting for server to shut down..."));
/* always wait for restart */ /* always wait for restart */

View File

@ -140,9 +140,9 @@ static const struct exclude_list_item excludeFiles[] =
{"pg_internal.init", true}, /* defined as RELCACHE_INIT_FILENAME */ {"pg_internal.init", true}, /* defined as RELCACHE_INIT_FILENAME */
/* /*
* If there's a backup_label or tablespace_map file, it belongs to a * If there is a backup_label or tablespace_map file, it indicates that
* backup started by the user with pg_start_backup(). It is *not* correct * a recovery failed and this cluster probably can't be rewound, but
* for this backup. Our backup_label is written later on separately. * exclude them anyway if they are found.
*/ */
{"backup_label", false}, /* defined as BACKUP_LABEL_FILE */ {"backup_label", false}, /* defined as BACKUP_LABEL_FILE */
{"tablespace_map", false}, /* defined as TABLESPACE_MAP */ {"tablespace_map", false}, /* defined as TABLESPACE_MAP */

View File

@ -276,14 +276,13 @@ extern void XLogShutdownWalRcv(void);
typedef enum SessionBackupState typedef enum SessionBackupState
{ {
SESSION_BACKUP_NONE, SESSION_BACKUP_NONE,
SESSION_BACKUP_EXCLUSIVE, SESSION_BACKUP_RUNNING,
SESSION_BACKUP_NON_EXCLUSIVE
} SessionBackupState; } SessionBackupState;
extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast, extern XLogRecPtr do_pg_backup_start(const char *backupidstr, bool fast,
TimeLineID *starttli_p, StringInfo labelfile, TimeLineID *starttli_p, StringInfo labelfile,
List **tablespaces, StringInfo tblspcmapfile); List **tablespaces, StringInfo tblspcmapfile);
extern XLogRecPtr do_pg_stop_backup(char *labelfile, bool waitforarchive, extern XLogRecPtr do_pg_backup_stop(char *labelfile, bool waitforarchive,
TimeLineID *stoptli_p); TimeLineID *stoptli_p);
extern void do_pg_abort_backup(int code, Datum arg); extern void do_pg_abort_backup(int code, Datum arg);
extern void register_persistent_abort_backup_handler(void); extern void register_persistent_abort_backup_handler(void);

View File

@ -161,9 +161,7 @@ typedef struct ControlFileData
* *
* If backupEndRequired is true, we know for sure that we're restoring * If backupEndRequired is true, we know for sure that we're restoring
* from a backup, and must see a backup-end record before we can safely * from a backup, and must see a backup-end record before we can safely
* start up. If it's false, but backupStartPoint is set, a backup_label * start up.
* file was found at startup but it may have been a leftover from a stray
* pg_start_backup() call, not accompanied by pg_stop_backup().
*/ */
XLogRecPtr minRecoveryPoint; XLogRecPtr minRecoveryPoint;
TimeLineID minRecoveryPointTLI; TimeLineID minRecoveryPointTLI;

View File

@ -6274,26 +6274,16 @@
proargtypes => 'int4 int8', proargnames => '{pid,timeout}', proargtypes => 'int4 int8', proargnames => '{pid,timeout}',
prosrc => 'pg_terminate_backend' }, prosrc => 'pg_terminate_backend' },
{ oid => '2172', descr => 'prepare for taking an online backup', { oid => '2172', descr => 'prepare for taking an online backup',
proname => 'pg_start_backup', provolatile => 'v', proparallel => 'r', proname => 'pg_backup_start', provolatile => 'v', proparallel => 'r',
prorettype => 'pg_lsn', proargtypes => 'text bool bool', prorettype => 'pg_lsn', proargtypes => 'text bool',
prosrc => 'pg_start_backup' }, prosrc => 'pg_backup_start' },
{ oid => '2173', descr => 'finish taking an online backup',
proname => 'pg_stop_backup', provolatile => 'v', proparallel => 'r',
prorettype => 'pg_lsn', proargtypes => '', prosrc => 'pg_stop_backup' },
{ oid => '2739', descr => 'finish taking an online backup', { oid => '2739', descr => 'finish taking an online backup',
proname => 'pg_stop_backup', provolatile => 'v', proparallel => 'r', proname => 'pg_backup_stop', provolatile => 'v', proparallel => 'r',
prorettype => 'record', proargtypes => 'bool bool', prorettype => 'record', proargtypes => 'bool',
proallargtypes => '{bool,bool,pg_lsn,text,text}', proallargtypes => '{bool,pg_lsn,text,text}',
proargmodes => '{i,i,o,o,o}', proargmodes => '{i,o,o,o}',
proargnames => '{exclusive,wait_for_archive,lsn,labelfile,spcmapfile}', proargnames => '{wait_for_archive,lsn,labelfile,spcmapfile}',
prosrc => 'pg_stop_backup_v2' }, prosrc => 'pg_backup_stop' },
{ oid => '3813', descr => 'true if server is in online backup',
proname => 'pg_is_in_backup', provolatile => 'v', prorettype => 'bool',
proargtypes => '', prosrc => 'pg_is_in_backup' },
{ oid => '3814', descr => 'start time of an online backup',
proname => 'pg_backup_start_time', provolatile => 's',
prorettype => 'timestamptz', proargtypes => '',
prosrc => 'pg_backup_start_time' },
{ oid => '3436', descr => 'promote standby server', { oid => '3436', descr => 'promote standby server',
proname => 'pg_promote', provolatile => 'v', prorettype => 'bool', proname => 'pg_promote', provolatile => 'v', prorettype => 'bool',
proargtypes => 'bool int4', proargnames => '{wait,wait_seconds}', proargtypes => 'bool int4', proargnames => '{wait,wait_seconds}',

View File

@ -75,8 +75,7 @@ typedef enum CAC_state
CAC_SHUTDOWN, CAC_SHUTDOWN,
CAC_RECOVERY, CAC_RECOVERY,
CAC_NOTCONSISTENT, CAC_NOTCONSISTENT,
CAC_TOOMANY, CAC_TOOMANY
CAC_SUPERUSER
} CAC_state; } CAC_state;

View File

@ -481,10 +481,6 @@ extern void process_session_preload_libraries(void);
extern void pg_bindtextdomain(const char *domain); extern void pg_bindtextdomain(const char *domain);
extern bool has_rolreplication(Oid roleid); extern bool has_rolreplication(Oid roleid);
/* in access/transam/xlog.c */
extern bool BackupInProgress(void);
extern void CancelBackup(void);
/* in executor/nodeHash.c */ /* in executor/nodeHash.c */
extern size_t get_hash_memory_limit(void); extern size_t get_hash_memory_limit(void);

View File

@ -638,25 +638,6 @@ sub backup
return; return;
} }
=item $node->backup_fs_hot(backup_name)
Create a backup with a filesystem level copy in subdirectory B<backup_name> of
B<< $node->backup_dir >>, including WAL.
Archiving must be enabled, as B<pg_start_backup()> and B<pg_stop_backup()> are
used. This is not checked or enforced.
The backup name is passed as the backup label to B<pg_start_backup()>.
=cut
sub backup_fs_hot
{
my ($self, $backup_name) = @_;
$self->_backup_fs($backup_name, 1);
return;
}
=item $node->backup_fs_cold(backup_name) =item $node->backup_fs_cold(backup_name)
Create a backup with a filesystem level copy in subdirectory B<backup_name> of Create a backup with a filesystem level copy in subdirectory B<backup_name> of
@ -670,53 +651,18 @@ Use B<backup> or B<backup_fs_hot> if you want to back up a running server.
sub backup_fs_cold sub backup_fs_cold
{ {
my ($self, $backup_name) = @_; my ($self, $backup_name) = @_;
$self->_backup_fs($backup_name, 0);
return;
}
# Common sub of backup_fs_hot and backup_fs_cold
sub _backup_fs
{
my ($self, $backup_name, $hot) = @_;
my $backup_path = $self->backup_dir . '/' . $backup_name;
my $port = $self->port;
my $name = $self->name;
print "# Taking filesystem backup $backup_name from node \"$name\"\n";
if ($hot)
{
my $stdout = $self->safe_psql('postgres',
"SELECT * FROM pg_start_backup('$backup_name');");
print "# pg_start_backup: $stdout\n";
}
PostgreSQL::Test::RecursiveCopy::copypath( PostgreSQL::Test::RecursiveCopy::copypath(
$self->data_dir, $self->data_dir,
$backup_path, $self->backup_dir . '/' . $backup_name,
filterfn => sub { filterfn => sub {
my $src = shift; my $src = shift;
return ($src ne 'log' and $src ne 'postmaster.pid'); return ($src ne 'log' and $src ne 'postmaster.pid');
}); });
if ($hot)
{
# We ignore pg_stop_backup's return value. We also assume archiving
# is enabled; otherwise the caller will have to copy the remaining
# segments.
my $stdout =
$self->safe_psql('postgres', 'SELECT * FROM pg_stop_backup();');
print "# pg_stop_backup: $stdout\n";
}
print "# Backup finished\n";
return; return;
} }
=pod =pod
=item $node->init_from_backup(root_node, backup_name) =item $node->init_from_backup(root_node, backup_name)

View File

@ -69,7 +69,9 @@ $node_primary->safe_psql('dropme',
$node_primary->safe_psql('postgres', 'CHECKPOINT;'); $node_primary->safe_psql('postgres', 'CHECKPOINT;');
my $backup_name = 'b1'; my $backup_name = 'b1';
$node_primary->backup_fs_hot($backup_name); $node_primary->stop();
$node_primary->backup_fs_cold($backup_name);
$node_primary->start();
$node_primary->safe_psql('postgres', $node_primary->safe_psql('postgres',
q[SELECT pg_create_physical_replication_slot('phys_slot');]); q[SELECT pg_create_physical_replication_slot('phys_slot');]);