Skip to content

Commit 1257184

Browse files
committed
Merge branch 'master' into issue_174
2 parents 62024cc + da283d3 commit 1257184

File tree

7 files changed

+220
-18
lines changed

7 files changed

+220
-18
lines changed

doc/pgprobackup.xml

+61-6
Original file line numberDiff line numberDiff line change
@@ -1634,13 +1634,14 @@ pg_probackup restore -B <replaceable>backup_dir</replaceable> --instance <replac
16341634
<para>
16351635
If you restore <link linkend="pbk-archive-mode">ARCHIVE</link> backups,
16361636
perform <link linkend="pbk-performing-point-in-time-pitr-recovery">PITR</link>,
1637-
or specify the <literal>--restore-as-replica</literal> option with the
1637+
or specify the <literal>--restore-as-replica</literal> flag with the
16381638
<literal>restore</literal> command to set up a standby server,
16391639
<application>pg_probackup</application> creates a recovery configuration
16401640
file once all data files are copied into the target directory. This file
16411641
includes the minimal settings required for recovery, except for the password in the
16421642
<ulink url="https://postgrespro.com/docs/postgresql/current/runtime-config-replication.html#GUC-PRIMARY-CONNINFO">primary_conninfo</ulink>
1643-
parameter; you have to add the password manually, if required.
1643+
parameter; you have to add the password manually or use
1644+
the <literal>--primary-conninfo</literal> option, if required.
16441645
For <productname>PostgreSQL</productname> 11 or lower,
16451646
recovery settings are written into the <filename>recovery.conf</filename>
16461647
file. Starting from <productname>PostgreSQL</productname> 12,
@@ -2242,6 +2243,13 @@ BACKUP INSTANCE 'node'
22422243
<literal>MERGING</literal> — the backup is being merged.
22432244
</para>
22442245
</listitem>
2246+
<listitem>
2247+
<para>
2248+
<literal>MERGED</literal> — the backup data files were
2249+
successfully merged, but its metadata is in the process
2250+
of been updated. Only full backup can have this status.
2251+
</para>
2252+
</listitem>
22452253
<listitem>
22462254
<para>
22472255
<literal>DELETING</literal> — the backup files are being deleted.
@@ -3146,7 +3154,9 @@ pg_probackup merge -B <replaceable>backup_dir</replaceable> --instance <replacea
31463154
<para>
31473155
This command merges the specified incremental backup to its
31483156
parent full backup, together with all incremental backups
3149-
between them. Once the merge is complete, the incremental
3157+
between them. If the specified backup ID belong to the full backup,
3158+
then it will be merged with the closest incremental backup.
3159+
Once the merge is complete, the incremental
31503160
backups are removed as redundant. Thus, the merge operation is
31513161
virtually equivalent to retaking a full backup and removing all
31523162
the outdated backups, but it allows to save much time,
@@ -3165,7 +3175,8 @@ pg_probackup show -B <replaceable>backup_dir</replaceable> --instance <replaceab
31653175
</programlisting>
31663176
<para>
31673177
If the merge is still in progress, the backup status is
3168-
displayed as <literal>MERGING</literal>. The merge is idempotent, so you can
3178+
displayed as <literal>MERGING</literal> or, at the final stage,
3179+
<literal>MERGED</literal>. The merge is idempotent, so you can
31693180
restart the merge if it was interrupted.
31703181
</para>
31713182
</refsect2>
@@ -3404,6 +3415,7 @@ pg_probackup backup -B <replaceable>backup_dir</replaceable> -b <replaceable>bac
34043415
[--no-validate] [--skip-block-validation]
34053416
[-w --no-password] [-W --password]
34063417
[--archive-timeout=<replaceable>timeout</replaceable>] [--external-dirs=<replaceable>external_directory_path</replaceable>]
3418+
[--no-sync]
34073419
[<replaceable>connection_options</replaceable>] [<replaceable>compression_options</replaceable>] [<replaceable>remote_options</replaceable>]
34083420
[<replaceable>retention_options</replaceable>] [<replaceable>pinning_options</replaceable>] [<replaceable>logging_options</replaceable>]
34093421
</programlisting>
@@ -3556,6 +3568,18 @@ pg_probackup backup -B <replaceable>backup_dir</replaceable> -b <replaceable>bac
35563568
</para>
35573569
</listitem>
35583570
</varlistentry>
3571+
3572+
<varlistentry>
3573+
<term><option>--no-sync</option></term>
3574+
<listitem>
3575+
<para>
3576+
Do not sync backed up files to disk. You can use this flag to speed
3577+
up backup process. Using this flag can result in data
3578+
corruption in case of operating system or hardware crash.
3579+
Corruption can be detected by backup validation.
3580+
</para>
3581+
</listitem>
3582+
</varlistentry>
35593583
</variablelist>
35603584
</para>
35613585

@@ -3582,8 +3606,11 @@ pg_probackup restore -B <replaceable>backup_dir</replaceable> --instance <replac
35823606
[--help] [-D <replaceable>data_dir</replaceable>] [-i <replaceable>backup_id</replaceable>]
35833607
[-j <replaceable>num_threads</replaceable>] [--progress]
35843608
[-T <replaceable>OLDDIR</replaceable>=<replaceable>NEWDIR</replaceable>] [--external-mapping=<replaceable>OLDDIR</replaceable>=<replaceable>NEWDIR</replaceable>] [--skip-external-dirs]
3585-
[-R | --restore-as-replica] [--no-validate] [--skip-block-validation] [--force]
3609+
[-R | --restore-as-replica] [--no-validate] [--skip-block-validation]
3610+
[--force] [--no-sync]
3611+
[--restore-command=<replaceable>cmdline</replaceable>]
35863612
[--restore-command=<replaceable>cmdline</replaceable>]
3613+
[--primary-conninfo=<replaceable>primary_conninfo</replaceable>]
35873614
[<replaceable>recovery_target_options</replaceable>] [<replaceable>logging_options</replaceable>] [<replaceable>remote_options</replaceable>]
35883615
[<replaceable>partial_restore_options</replaceable>] [<replaceable>remote_wal_archive_options</replaceable>]
35893616
</programlisting>
@@ -3621,6 +3648,21 @@ pg_probackup restore -B <replaceable>backup_dir</replaceable> --instance <replac
36213648
</listitem>
36223649
</varlistentry>
36233650

3651+
<varlistentry>
3652+
<term><option>--primary-conninfo=<replaceable>primary_conninfo</replaceable></option></term>
3653+
<listitem>
3654+
<para>
3655+
Sets the
3656+
<ulink url="https://postgrespro.com/docs/postgresql/current/runtime-config-replication.html#GUC-PRIMARY-CONNINFO">primary_conninfo</ulink>
3657+
parameter to the specified value.
3658+
This option will be ignored unless the <option>-R</option> flag if specified.
3659+
</para>
3660+
<para>
3661+
Example: <literal>--primary-conninfo='host=192.168.1.50 port=5432 user=foo password=foopass'</literal>
3662+
</para>
3663+
</listitem>
3664+
</varlistentry>
3665+
36243666
<varlistentry>
36253667
<term><option>-T <replaceable>OLDDIR</replaceable>=<replaceable>NEWDIR</replaceable></option></term>
36263668
<term><option>--tablespace-mapping=<replaceable>OLDDIR</replaceable>=<replaceable>NEWDIR</replaceable></option></term>
@@ -3705,6 +3747,17 @@ pg_probackup restore -B <replaceable>backup_dir</replaceable> --instance <replac
37053747
</para>
37063748
</listitem>
37073749
</varlistentry>
3750+
3751+
<varlistentry>
3752+
<term><option>--no-sync</option></term>
3753+
<listitem>
3754+
<para>
3755+
Do not sync restored files to disk. You can use this flag to speed
3756+
up restore process. Using this flag can result in data
3757+
corruption in case of operating system or hardware crash.
3758+
</para>
3759+
</listitem>
3760+
</varlistentry>
37083761
</variablelist>
37093762
</para>
37103763
<para>
@@ -3831,7 +3884,9 @@ pg_probackup merge -B <replaceable>backup_dir</replaceable> --instance <replacea
38313884
<para>
38323885
Merges the specified incremental backup to its parent full
38333886
backup, together with all incremental backups between them, if
3834-
any. As a result, the full backup takes in all the merged
3887+
any. If the specified backup ID belong to the full backup,
3888+
then it will be merged with the closest incremental backup.
3889+
As a result, the full backup takes in all the merged
38353890
data, and the incremental backups are removed as redundant.
38363891
</para>
38373892
<para>

src/merge.c

+26
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,27 @@ do_merge(time_t backup_id)
106106
}
107107
}
108108

109+
/*
110+
* Handle the case of crash right after deletion of the target
111+
* incremental backup. We still can recover from this.
112+
* Iterate over backups and look for the FULL backup with
113+
* MERGED status, that has merge-target-id eqial to backup_id.
114+
*/
115+
if (dest_backup == NULL)
116+
{
117+
for (i = 0; i < parray_num(backups); i++)
118+
{
119+
pgBackup *backup = (pgBackup *) parray_get(backups, i);
120+
121+
if (backup->status == BACKUP_STATUS_MERGED &&
122+
backup->merge_dest_backup == backup_id)
123+
{
124+
dest_backup = backup;
125+
break;
126+
}
127+
}
128+
}
129+
109130
if (dest_backup == NULL)
110131
elog(ERROR, "Target backup %s was not found", base36enc(backup_id));
111132

@@ -776,6 +797,11 @@ merge_chain(parray *parent_chain, pgBackup *full_backup, pgBackup *dest_backup)
776797
full_backup->root_dir, destination_path, strerror(errno));
777798
}
778799

800+
/* If we crash here, it will produce full backup in MERGED
801+
* status, located in directory with wrong backup id.
802+
* It should not be a problem.
803+
*/
804+
779805
/*
780806
* Merging finished, now we can safely update ID of the FULL backup
781807
*/

src/pg_probackup.c

+4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ static bool target_immediate;
9090
static char *target_name = NULL;
9191
static char *target_action = NULL;
9292

93+
static char *primary_conninfo = NULL;
94+
9395
static pgRecoveryTarget *recovery_target_options = NULL;
9496
static pgRestoreParams *restore_params = NULL;
9597

@@ -194,6 +196,7 @@ static ConfigOption cmd_options[] =
194196
{ 'b', 156, "skip-external-dirs", &skip_external_dirs, SOURCE_CMD_STRICT },
195197
{ 'f', 158, "db-include", opt_datname_include_list, SOURCE_CMD_STRICT },
196198
{ 'f', 159, "db-exclude", opt_datname_exclude_list, SOURCE_CMD_STRICT },
199+
{ 's', 160, "primary-conninfo", &primary_conninfo, SOURCE_CMD_STRICT },
197200
/* checkdb options */
198201
{ 'b', 195, "amcheck", &need_amcheck, SOURCE_CMD_STRICT },
199202
{ 'b', 196, "heapallindexed", &heapallindexed, SOURCE_CMD_STRICT },
@@ -690,6 +693,7 @@ main(int argc, char *argv[])
690693
restore_params->skip_external_dirs = skip_external_dirs;
691694
restore_params->partial_db_list = NULL;
692695
restore_params->partial_restore_type = NONE;
696+
restore_params->primary_conninfo = primary_conninfo;
693697

694698
/* handle partial restore parameters */
695699
if (datname_exclude_list && datname_include_list)

src/pg_probackup.h

+1
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ typedef struct pgRestoreParams
422422
/* options for partial restore */
423423
PartialRestoreType partial_restore_type;
424424
parray *partial_db_list;
425+
const char *primary_conninfo;
425426
} pgRestoreParams;
426427

427428
/* Options needed for set-backup command */

src/restore.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,9 @@ create_recovery_conf(time_t backup_id,
10591059
fio_fprintf(fp, "standby_mode = 'on'\n");
10601060
#endif
10611061

1062-
if (backup->primary_conninfo)
1062+
if (params->primary_conninfo)
1063+
fio_fprintf(fp, "primary_conninfo = '%s'\n", params->primary_conninfo);
1064+
else if (backup->primary_conninfo)
10631065
fio_fprintf(fp, "primary_conninfo = '%s'\n", backup->primary_conninfo);
10641066
}
10651067

tests/merge.py

+74-10
Original file line numberDiff line numberDiff line change
@@ -2308,17 +2308,81 @@ def test_smart_merge(self):
23082308
# Clean after yourself
23092309
self.del_test_dir(module_name, fname)
23102310

2311+
def test_idempotent_merge(self):
2312+
"""
2313+
"""
2314+
fname = self.id().split('.')[3]
2315+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
2316+
node = self.make_simple_node(
2317+
base_dir=os.path.join(module_name, fname, 'node'),
2318+
set_replication=True,
2319+
initdb_params=['--data-checksums'],
2320+
pg_options={'autovacuum': 'off'})
2321+
2322+
self.init_pb(backup_dir)
2323+
self.add_instance(backup_dir, 'node', node)
2324+
self.set_archiving(backup_dir, 'node', node)
2325+
node.slow_start()
2326+
2327+
# add database
2328+
node.safe_psql(
2329+
'postgres',
2330+
'CREATE DATABASE testdb')
2331+
2332+
# take FULL backup
2333+
full_id = self.backup_node(
2334+
backup_dir, 'node', node, options=['--stream'])
2335+
2336+
# create database
2337+
node.safe_psql(
2338+
'postgres',
2339+
'create DATABASE testdb1')
2340+
2341+
# take PAGE backup
2342+
page_id = self.backup_node(
2343+
backup_dir, 'node', node, backup_type='page')
2344+
2345+
# create database
2346+
node.safe_psql(
2347+
'postgres',
2348+
'create DATABASE testdb2')
2349+
2350+
page_id_2 = self.backup_node(
2351+
backup_dir, 'node', node, backup_type='page')
2352+
2353+
gdb = self.merge_backup(
2354+
backup_dir, 'node', page_id_2,
2355+
gdb=True, options=['--log-level-console=verbose'])
2356+
2357+
gdb.set_breakpoint('delete_backup_files')
2358+
gdb.run_until_break()
2359+
gdb.remove_all_breakpoints()
2360+
2361+
gdb.set_breakpoint('rename')
2362+
gdb.continue_execution_until_break()
2363+
gdb.continue_execution_until_break(2)
2364+
2365+
gdb._execute('signal SIGKILL')
2366+
2367+
show_backups = self.show_pb(backup_dir, "node")
2368+
self.assertEqual(len(show_backups), 1)
2369+
2370+
self.assertEqual(
2371+
'MERGED', self.show_pb(backup_dir, 'node')[0]['status'])
2372+
2373+
self.assertEqual(
2374+
full_id, self.show_pb(backup_dir, 'node')[0]['id'])
2375+
2376+
self.merge_backup(backup_dir, 'node', page_id_2)
2377+
2378+
self.assertEqual(
2379+
'OK', self.show_pb(backup_dir, 'node')[0]['status'])
23112380

2312-
# 1. always use parent link when merging (intermediates may be from different chain)
2313-
# 2. page backup we are merging with may disappear after failed merge,
2314-
# it should not be possible to continue merge after that
2315-
# PAGE_A MERGING (disappear)
2316-
# FULL MERGING
2381+
self.assertEqual(
2382+
page_id_2, self.show_pb(backup_dir, 'node')[0]['id'])
23172383

2318-
# FULL MERGING
23192384

2320-
# PAGE_B OK (new backup)
2321-
# FULL MERGING
2385+
self.del_test_dir(module_name, fname)
23222386

2323-
# 3. Need new test with corrupted FULL backup
2324-
# 4. different compression levels
2387+
# 1. Need new test with corrupted FULL backup
2388+
# 2. different compression levels

tests/restore.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -3376,4 +3376,54 @@ def test_stream_restore_command_option(self):
33763376
self.assertEqual('2', timeline_id)
33773377

33783378
# Clean after yourself
3379-
self.del_test_dir(module_name, fname)
3379+
self.del_test_dir(module_name, fname)
3380+
3381+
# @unittest.skip("skip")
3382+
def test_restore_primary_conninfo(self):
3383+
"""
3384+
"""
3385+
fname = self.id().split('.')[3]
3386+
node = self.make_simple_node(
3387+
base_dir=os.path.join(module_name, fname, 'node'),
3388+
set_replication=True,
3389+
initdb_params=['--data-checksums'])
3390+
3391+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
3392+
self.init_pb(backup_dir)
3393+
self.add_instance(backup_dir, 'node', node)
3394+
node.slow_start()
3395+
3396+
# Take FULL
3397+
self.backup_node(backup_dir, 'node', node, options=['--stream'])
3398+
3399+
node.pgbench_init(scale=1)
3400+
3401+
#primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
3402+
3403+
replica = self.make_simple_node(
3404+
base_dir=os.path.join(module_name, fname, 'replica'))
3405+
replica.cleanup()
3406+
str_conninfo='host=192.168.1.50 port=5432 user=foo password=foopass'
3407+
3408+
self.restore_node(
3409+
backup_dir, 'node', replica,
3410+
options=['-R', '--primary-conninfo={0}'.format(str_conninfo)])
3411+
3412+
if self.get_version(node) >= self.version_to_num('12.0'):
3413+
standby_signal = os.path.join(replica.data_dir, 'standby.signal')
3414+
self.assertTrue(
3415+
os.path.isfile(standby_signal),
3416+
"File '{0}' do not exists".format(standby_signal))
3417+
3418+
if self.get_version(node) >= self.version_to_num('12.0'):
3419+
recovery_conf = os.path.join(replica.data_dir, 'probackup_recovery.conf')
3420+
else:
3421+
recovery_conf = os.path.join(replica.data_dir, 'recovery.conf')
3422+
3423+
with open(os.path.join(replica.data_dir, recovery_conf), 'r') as f:
3424+
recovery_conf_content = f.read()
3425+
3426+
self.assertIn(str_conninfo, recovery_conf_content)
3427+
3428+
# Clean after yourself
3429+
self.del_test_dir(module_name, fname)

0 commit comments

Comments
 (0)