143 lines
5.5 KiB
Perl
143 lines
5.5 KiB
Perl
# Copyright (c) 2021-2024, PostgreSQL Global Development Group
|
|
#
|
|
# This test aims to validate that an incremental backup can be combined
|
|
# with a valid prior backup and that it cannot be combined with an invalid
|
|
# prior backup.
|
|
|
|
use strict;
|
|
use warnings FATAL => 'all';
|
|
use File::Compare;
|
|
use File::Path qw(rmtree);
|
|
use File::Copy;
|
|
use PostgreSQL::Test::Cluster;
|
|
use PostgreSQL::Test::Utils;
|
|
use Test::More;
|
|
|
|
# Set up a new database instance.
|
|
my $node1 = PostgreSQL::Test::Cluster->new('node1');
|
|
$node1->init(has_archiving => 1, allows_streaming => 1);
|
|
$node1->append_conf('postgresql.conf', 'summarize_wal = on');
|
|
$node1->start;
|
|
|
|
# Create a file called INCREMENTAL.config in the root directory of the
|
|
# first database instance. We only recognize INCREMENTAL.${original_name}
|
|
# files under base and global and in tablespace directories, so this shouldn't
|
|
# cause anything to fail.
|
|
my $strangely_named_config_file = $node1->data_dir . '/INCREMENTAL.config';
|
|
open(my $icfg, '>', $strangely_named_config_file)
|
|
|| die "$strangely_named_config_file: $!";
|
|
close($icfg);
|
|
|
|
# Set up another new database instance. force_initdb is used because
|
|
# we want it to be a separate cluster with a different system ID.
|
|
my $node2 = PostgreSQL::Test::Cluster->new('node2');
|
|
$node2->init(force_initdb => 1, has_archiving => 1, allows_streaming => 1);
|
|
$node2->append_conf('postgresql.conf', 'summarize_wal = on');
|
|
$node2->start;
|
|
|
|
# Take a full backup from node1.
|
|
my $backup1path = $node1->backup_dir . '/backup1';
|
|
$node1->command_ok(
|
|
[ 'pg_basebackup', '-D', $backup1path, '--no-sync', '-cfast' ],
|
|
"full backup from node1");
|
|
|
|
# Now take an incremental backup.
|
|
my $backup2path = $node1->backup_dir . '/backup2';
|
|
$node1->command_ok(
|
|
[ 'pg_basebackup', '-D', $backup2path, '--no-sync', '-cfast',
|
|
'--incremental', $backup1path . '/backup_manifest' ],
|
|
"incremental backup from node1");
|
|
|
|
# Now take another incremental backup.
|
|
my $backup3path = $node1->backup_dir . '/backup3';
|
|
$node1->command_ok(
|
|
[ 'pg_basebackup', '-D', $backup3path, '--no-sync', '-cfast',
|
|
'--incremental', $backup2path . '/backup_manifest' ],
|
|
"another incremental backup from node1");
|
|
|
|
# Take a full backup from node2.
|
|
my $backupother1path = $node1->backup_dir . '/backupother1';
|
|
$node2->command_ok(
|
|
[ 'pg_basebackup', '-D', $backupother1path, '--no-sync', '-cfast' ],
|
|
"full backup from node2");
|
|
|
|
# Take an incremental backup from node2.
|
|
my $backupother2path = $node1->backup_dir . '/backupother2';
|
|
$node2->command_ok(
|
|
[ 'pg_basebackup', '-D', $backupother2path, '--no-sync', '-cfast',
|
|
'--incremental', $backupother1path . '/backup_manifest' ],
|
|
"incremental backup from node2");
|
|
|
|
# Result directory.
|
|
my $resultpath = $node1->backup_dir . '/result';
|
|
|
|
# Can't combine 2 full backups.
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $backup1path, $backup1path, '-o', $resultpath ],
|
|
qr/is a full backup, but only the first backup should be a full backup/,
|
|
"can't combine full backups");
|
|
|
|
# Can't combine 2 incremental backups.
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $backup2path, $backup2path, '-o', $resultpath ],
|
|
qr/is an incremental backup, but the first backup should be a full backup/,
|
|
"can't combine full backups");
|
|
|
|
# Can't combine full backup with an incremental backup from a different system.
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $backup1path, $backupother2path, '-o', $resultpath ],
|
|
qr/expected system identifier.*but found/,
|
|
"can't combine backups from different nodes");
|
|
|
|
# Can't combine when different manifest system identifier
|
|
rename("$backup2path/backup_manifest", "$backup2path/backup_manifest.orig")
|
|
or die "could not move $backup2path/backup_manifest";
|
|
copy("$backupother2path/backup_manifest", "$backup2path/backup_manifest")
|
|
or die "could not copy $backupother2path/backup_manifest";
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $backup1path, $backup2path, $backup3path, '-o', $resultpath ],
|
|
qr/ manifest system identifier is .*, but control file has /,
|
|
"can't combine backups with different manifest system identifier ");
|
|
# Restore the backup state
|
|
move("$backup2path/backup_manifest.orig", "$backup2path/backup_manifest")
|
|
or die "could not move $backup2path/backup_manifest";
|
|
|
|
# Can't omit a required backup.
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $backup1path, $backup3path, '-o', $resultpath ],
|
|
qr/starts at LSN.*but expected/,
|
|
"can't omit a required backup");
|
|
|
|
# Can't combine backups in the wrong order.
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $backup1path, $backup3path, $backup2path, '-o', $resultpath ],
|
|
qr/starts at LSN.*but expected/,
|
|
"can't combine backups in the wrong order");
|
|
|
|
# Can combine 3 backups that match up properly.
|
|
$node1->command_ok(
|
|
[ 'pg_combinebackup', $backup1path, $backup2path, $backup3path, '-o', $resultpath ],
|
|
"can combine 3 matching backups");
|
|
rmtree($resultpath);
|
|
|
|
# Can combine full backup with first incremental.
|
|
my $synthetic12path = $node1->backup_dir . '/synthetic12';
|
|
$node1->command_ok(
|
|
[ 'pg_combinebackup', $backup1path, $backup2path, '-o', $synthetic12path ],
|
|
"can combine 2 matching backups");
|
|
|
|
# Can combine result of previous step with second incremental.
|
|
$node1->command_ok(
|
|
[ 'pg_combinebackup', $synthetic12path, $backup3path, '-o', $resultpath ],
|
|
"can combine synthetic backup with later incremental");
|
|
rmtree($resultpath);
|
|
|
|
# Can't combine result of 1+2 with 2.
|
|
$node1->command_fails_like(
|
|
[ 'pg_combinebackup', $synthetic12path, $backup2path, '-o', $resultpath ],
|
|
qr/starts at LSN.*but expected/,
|
|
"can't combine synthetic backup with included incremental");
|
|
|
|
# OK, that's all.
|
|
done_testing();
|