btrbk: always preserve latest snapshot/backup pair (instead of latest snapshot and latest backup); remove preserve_latest option of schedule()

pull/88/head
Axel Burri 2016-04-13 14:47:38 +02:00
parent 032642751b
commit 36cc96fdb8
3 changed files with 60 additions and 59 deletions

66
btrbk
View File

@ -1807,20 +1807,30 @@ sub vinfo_subsection($$;$)
} }
sub get_snapshot_children($$) sub get_snapshot_children($$;$$)
{ {
my $sroot = shift || die; my $sroot = shift || die;
my $svol = shift // die; my $svol = shift // die;
my $subvol_dir = shift // "";
my $btrbk_basename = shift;
my @ret; my @ret;
my $sroot_subvols = vinfo_subvol_list($sroot); my $sroot_subvols = vinfo_subvol_list($sroot);
foreach (@$sroot_subvols) { foreach (@$sroot_subvols) {
next unless($_->{node}{readonly}); next unless($_->{node}{readonly});
next unless($_->{node}{parent_uuid} eq $svol->{node}{uuid}); next unless($_->{node}{parent_uuid} eq $svol->{node}{uuid});
if(defined($btrbk_basename) &&
( (not exists($_->{BTRBK_BASENAME})) ||
($_->{SUBVOL_DIR} ne $subvol_dir) ||
($_->{BTRBK_BASENAME} ne $btrbk_basename)) ) {
TRACE "get_snapshot_children: child does not match btrbk filename scheme, skipping: $_->{PRINT}";
next;
}
TRACE "get_snapshot_children: found: $_->{PRINT}"; TRACE "get_snapshot_children: found: $_->{PRINT}";
push(@ret, $_); push(@ret, $_);
} }
DEBUG "Found " . scalar(@ret) . " snapshot children of: $svol->{PRINT}"; $subvol_dir .= '/' if($subvol_dir);
DEBUG "Found " . scalar(@ret) . " snapshot children of \"$svol->{PRINT}\" in: $sroot->{PRINT}" . (defined($btrbk_basename) ? "/$subvol_dir$btrbk_basename.*" : "");
return @ret; return @ret;
} }
@ -2651,7 +2661,6 @@ sub schedule(@)
my %args = @_; my %args = @_;
my $schedule = $args{schedule} || die; my $schedule = $args{schedule} || die;
my $preserve = $args{preserve} || die; my $preserve = $args{preserve} || die;
my $preserve_latest = $args{preserve_latest} || 0;
my $results_list = $args{results}; my $results_list = $args{results};
my $result_hints = $args{result_hints} // {}; my $result_hints = $args{result_hints} // {};
@ -2699,11 +2708,6 @@ sub schedule(@)
} }
} }
if($preserve_latest && (scalar @sorted_schedule)) {
my $href = $sorted_schedule[-1];
$href->{preserve} ||= $preserve_latest;
}
my %first_in_delta_hours; my %first_in_delta_hours;
my %first_in_delta_days; my %first_in_delta_days;
my %first_in_delta_weeks; my %first_in_delta_weeks;
@ -4229,7 +4233,8 @@ MAIN:
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) { foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
my $snapdir = config_key($svol, "snapshot_dir") // ""; my $snapdir = config_key($svol, "snapshot_dir") // "";
my $snapshot_basename = config_key($svol, "snapshot_name") // die; my $snapshot_basename = config_key($svol, "snapshot_name") // die;
my $preserve_latest = $svol->{SNAPSHOT_CREATED} ? 0 : 1; my @snapshot_children = sort({ cmp_date($a->{BTRBK_DATE}, $b->{BTRBK_DATE}) }
get_snapshot_children($sroot, $svol, $snapdir, $snapshot_basename));
foreach my $droot (vinfo_subsection($svol, 'target')) { foreach my $droot (vinfo_subsection($svol, 'target')) {
INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/"; INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/";
@ -4237,14 +4242,8 @@ MAIN:
my $resume_total = 0; my $resume_total = 0;
my $resume_success = 0; my $resume_success = 0;
foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } get_snapshot_children($sroot, $svol)) foreach my $child (@snapshot_children)
{ {
unless($child->{BTRBK_DATE} &&
($child->{SUBVOL_DIR} eq $snapdir) &&
($child->{BTRBK_BASENAME} eq $snapshot_basename)) {
TRACE "Backup candidate does not match btrbk filename scheme, skipping: $child->{PRINT}";
next;
}
next if(scalar(get_receive_targets($droot, $child, exact_match => 1, warn_unexpected => 1))); next if(scalar(get_receive_targets($droot, $child, exact_match => 1, warn_unexpected => 1)));
if(my $err_vol = vinfo_subvol($droot, $child->{NAME})) { if(my $err_vol = vinfo_subvol($droot, $child->{NAME})) {
@ -4272,13 +4271,13 @@ MAIN:
} }
push(@schedule, { value => undef, push(@schedule, { value => undef,
btrbk_date => $vol->{BTRBK_DATE}, btrbk_date => $vol->{BTRBK_DATE},
preserve => $vol->{node}{FORCE_PRESERVE}, # not enforcing resuming of latest snapshot anymore (since v0.23.0)
# preserve => $vol->{node}{FORCE_PRESERVE},
}); });
} }
my ($preserve, undef) = schedule( my ($preserve, undef) = schedule(
schedule => \@schedule, schedule => \@schedule,
preserve => config_preserve_hash($droot, "target"), preserve => config_preserve_hash($droot, "target"),
preserve_latest => $preserve_latest,
); );
my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes) my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes)
$resume_total = scalar @resume; $resume_total = scalar @resume;
@ -4324,11 +4323,12 @@ MAIN:
{ {
foreach my $sroot (vinfo_subsection($config, 'volume')) { foreach my $sroot (vinfo_subsection($config, 'volume')) {
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) { foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
my $snapdir = config_key($svol, "snapshot_dir") // "";
my $snapdir_ts = config_key($svol, "snapshot_dir", postfix => '/') // ""; my $snapdir_ts = config_key($svol, "snapshot_dir", postfix => '/') // "";
my $snapshot_basename = config_key($svol, "snapshot_name") // die; my $snapshot_basename = config_key($svol, "snapshot_name") // die;
my $preserve_latest_snapshot = $svol->{SNAPSHOT_CREATED} ? 0 : "preserve forced: latest in list (no snapshot created)";
my $preserve_latest_backup = $preserve_latest_snapshot;
my $target_aborted = 0; my $target_aborted = 0;
my @snapshot_children = sort({ cmp_date($b->{BTRBK_DATE}, $a->{BTRBK_DATE}) } # sort descending
get_snapshot_children($sroot, $svol, $snapdir, $snapshot_basename));
foreach my $droot (vinfo_subsection($svol, 'target', 1)) { foreach my $droot (vinfo_subsection($svol, 'target', 1)) {
if(ABORTED($droot)) { if(ABORTED($droot)) {
@ -4339,16 +4339,18 @@ MAIN:
} }
next; next;
} }
if($droot->{CONFIG}->{target_type} eq "raw") {
if(config_key($droot, "incremental")) { # always preserve latest common snapshot/backup pair
# In incremental mode, the latest backup is most certainly our parent. foreach my $child (@snapshot_children) {
# (see note on FORCE_PRESERVE above) my @receive_targets = get_receive_targets($droot, $child);
$preserve_latest_backup ||= "preserve forced: possibly parent of latest backup"; if(scalar(@receive_targets)) {
# Note that we could check against $svol->{SNAPSHOT_CREATED}->{node}{parent_uuid} to be certain, DEBUG "Force preserve for latest common snapshot: $child->{PRINT}";
# but this information is not available in $dryrun: $child->{node}{FORCE_PRESERVE} = 'preserve forced: latest common snapshot';
# foreach my $vol (@{vinfo_subvol_list($droot)}) { foreach(@receive_targets) {
# $vol->{FORCE_PRESERVE} = 1 if($vol->{node}{received_uuid} eq $svol->{SNAPSHOT_CREATED}->{node}{parent_uuid}); DEBUG "Force preserve for latest common target: $_->{PRINT}";
# } $_->{node}{FORCE_PRESERVE} = 'preserve forced: latest common target';
}
last;
} }
} }
@ -4358,7 +4360,6 @@ MAIN:
INFO "Cleaning backups of subvolume \"$svol->{PRINT}\": $droot->{PRINT}/$snapshot_basename.*"; INFO "Cleaning backups of subvolume \"$svol->{PRINT}\": $droot->{PRINT}/$snapshot_basename.*";
unless(macro_delete($droot, "", $snapshot_basename, $droot, unless(macro_delete($droot, "", $snapshot_basename, $droot,
{ preserve => config_preserve_hash($droot, "target"), { preserve => config_preserve_hash($droot, "target"),
preserve_latest => $preserve_latest_backup,
results => $schedule_results, results => $schedule_results,
result_hints => { topic => "backup", root_path => $droot->{PATH} }, result_hints => { topic => "backup", root_path => $droot->{PATH} },
}, },
@ -4382,9 +4383,8 @@ MAIN:
next; next;
} }
INFO "Cleaning snapshots: $sroot->{PRINT}/$snapdir_ts$snapshot_basename.*"; INFO "Cleaning snapshots: $sroot->{PRINT}/$snapdir_ts$snapshot_basename.*";
macro_delete($sroot, $snapdir_ts, $snapshot_basename, $svol, macro_delete($sroot, $snapdir, $snapshot_basename, $svol,
{ preserve => config_preserve_hash($svol, "snapshot"), { preserve => config_preserve_hash($svol, "snapshot"),
preserve_latest => $preserve_latest_snapshot,
results => $schedule_results, results => $schedule_results,
result_hints => { topic => "snapshot", root_path => $sroot->{PATH} }, result_hints => { topic => "snapshot", root_path => $sroot->{PATH} },
}, },

View File

@ -126,22 +126,23 @@ First, btrbk reads information from the source and target btrfs
filesystems in order to perform sanity checks and identify filesystems in order to perform sanity checks and identify
parent/child and received-from relationships. parent/child and received-from relationships.
.PP .PP
If the checks succeed, btrbk creates snapshots for all the source If the checks succeed, btrbk creates snapshots for the source
subvolumes specified in the configuration file. subvolumes specified in the configuration file, according to the
\fIsnapshot_create\fR option.
.PP .PP
Then, for each specified target, btrbk creates the backups as follows: Then, for each specified target, btrbk creates the backups as follows:
After the backups in the target directory are compared to the source After comparing the backups to the source snapshots, btrbk transfers
snapshots, btrbk transfers all missing snapshots needed to satisfy the all missing snapshots needed to satisfy the configured target
configured target retention policy, incrementally from the latest retention policy, incrementally from the latest common parent
common parent subvolume found. If no common parent subvolume is found subvolume found. If no common parent subvolume is found (or if the
(or if the \fIincremental\fR option is set to \[lq]no\[rq]), a full \fIincremental\fR option is set to \[lq]no\[rq]), a full
(non-incremental) backup is created. (non-incremental) backup is created.
.PP .PP
As a last step, unless the \-p (\-\-preserve) option is set, snapshots As a last step, unless the \-p (\-\-preserve) option is set, snapshots
and backup subvolumes that are not preserved by their configured and backup subvolumes that are not preserved by their configured
retention policy will be deleted. Note that the latest snapshot (the retention policy will be deleted. Note that the latest snapshot (the
one created in the first step) is always preserved, regardless of the one created in the first step) as well as the latest snapshot/backup
retention policy. pair are always preserved, regardless of the retention policy.
.PP .PP
Use the \fI\-\-format\fR command line option to switch between Use the \fI\-\-format\fR command line option to switch between
different output formats. different output formats.

View File

@ -100,16 +100,16 @@ valid in the \fIsubvolume\fR section. Defaults to
\fI<subvolume-name>\fR. \fI<subvolume-name>\fR.
.RE .RE
.PP .PP
\fBsnapshot_create\fR always|ondemand|onchange|no \fBsnapshot_create\fR always|onchange|ondemand|no
.RS 4 .RS 4
If set to \[lq]ondemand\[rq], snapshots are only created if the target If set to \[lq]always\[rq], snapshots are always created. If set to
subvolume is reachable (useful if you are tight on disk space and you \[lq]onchange\[rq], snapshots are only created if the source subvolume
only need btrbk for backups to an external disk which is not always has changed since the last snapshot (more precisely: if the btrfs
connected). If set to \[lq]onchange\[rq], snapshots are only created generation has been increased since the last snapshot). If set to
if the source subvolume has changed since the last snapshot (more \[lq]ondemand\[rq], snapshots are only created if the target subvolume
precisely: if the btrfs generation has been increased since the last is reachable (useful if you are tight on disk space and you only need
snapshot). If set to \[lq]always\[rq], snapshots are always btrbk for backups to an external disk which is not always
created. If set to \[lq]no\[rq], the snapshots are never created connected). If set to \[lq]no\[rq], the snapshots are never created
(useful if another instance of btrbk is taking care of snapshot (useful if another instance of btrbk is taking care of snapshot
creation). Defaults to \[lq]always\[rq]. creation). Defaults to \[lq]always\[rq].
.RE .RE