Skip to content

Fix selected differential backup chain state handling#1238

Merged
jcameron merged 9 commits into
masterfrom
dev/fix-differential-backups
Jul 3, 2026
Merged

Fix selected differential backup chain state handling#1238
jcameron merged 9 commits into
masterfrom
dev/fix-differential-backups

Conversation

@iliaross

Copy link
Copy Markdown
Member

Hello Jamie!

This fixes several edge cases in selected full-backup differential chains considering your latest changes to master!

Previously, full backup snapshot state could be advanced before the full backup was actually usable, or selected differential schedules could be configured against baselines that could not be represented or did not exist.

What changed (you can see more details in each linked commit):

  • Only advance the default differential snapshot after a chained full backup archive is successfully created.
  • Move full-backup snapshot promotion to the final backup result path, so state is advanced only after all destinations succeed.
  • Clear new full-backup snapshot state when the backup fails, including failures after tar has already updated state, such as remote upload failures.
  • Keep destination locks held until snapshot state has been updated.
  • Disallow using legacy scheduled backup ID 1 as a selected full-backup baseline, because increment == 1 already represents legacy differential mode.
  • Always keep per-schedule snapshot state for scheduled full backups so selected differential chains can be created later.
  • Fail selected differentials clearly when the chosen full-backup snapshot is missing, instead of silently creating a misleading backup from the wrong baseline.

iliaross added 4 commits June 22, 2026 14:03
ⓘ Only copy a per-backup incremental snapshot to the default snapshot after the full backup archive has been created successfully.

This prevents failed chained full backups from advancing the baseline used by later non-chained differential backups.
ⓘ Move chained full-backup snapshot promotion out of the per-domain archive step and into the final backup result path, so full backup state is advanced only after all destinations succeed.

Clear new full-backup state on failure, and keep destination locks held until state has been updated.
ⓘ Hide and reject legacy scheduled backup ID 1 as a selected full-backup baseline, since increment value 1 already represents legacy differential mode and cannot safely encode a schedule reference.
ⓘ Always keep per-schedule snapshot state for scheduled full backups so selected differential chains can be created later. Fail selected differentials clearly when the chosen full backup state is missing, avoiding misleading backups based on the wrong baseline.
Comment thread backups-lib.pl Outdated
# snapshot files, so failed full backups must not become a future baseline.
if ($increment == 0 && &has_incremental_tar()) {
foreach my $d (@errdoms) {
my %errids = map { $_->{'id'} ? ($_->{'id'}, 1) : () } @errdoms;

@jcameron jcameron Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty hard to read - best to use grep and map chained

@jcameron

Copy link
Copy Markdown
Collaborator

Didn't the old incremental backups before I added this feature already have the behavior that the incremental state could be advanced even before all destinations were complete?

@iliaross

Copy link
Copy Markdown
Member Author

Didn't the old incremental backups before I added this feature already have the behavior that the incremental state could be advanced even before all destinations were complete?

Yeah, tar used to advance the incremental snapshot during archive creation before all destinations were complete. If a later destination failed, the old code could leave that advanced snapshot in place even though the full backup was not usable everywhere.

@jcameron

jcameron commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

That's kind of a judgement call , because maybe the admin does prefer the current behavior if they are fine with at least one destination being OK. So maybe limit this PR to bugs in the new feature?

@iliaross

iliaross commented Jul 1, 2026

Copy link
Copy Markdown
Member Author

That's kind of a judgement call , because maybe the admin does prefer the current behavior if they are fine with at least one destination being OK. So maybe limit this PR to bugs in the new feature?

Ah, I see what you mean now! See this patch d88185d. It will keep selected full-backup snapshot state only after the backup reaches destination, while preserving old behavior.

@jcameron

jcameron commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Hang on, this is getting even more complex and we're fixing a whole bunch of separate issues in this PR that makes it hard to understand! Can we limit this to just bug fixes, and not change the existing behavior of when the incremental state is advanced? That may be something we want, but it should be in a separate PR.

@iliaross

iliaross commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

Can we limit this to just bug fixes, and not change the existing behavior of when the incremental state is advanced? That may be something we want, but it should be in a separate PR.

Okay. I've split it into its own #1246 PR that needs to go in once this one lands.

Comment thread backups-lib.pl Outdated
elsif ($mode == 0 && $id =~ /^\d+$/ && $id >= 3) {
# This is a scheduled full backup. Keep its own snapshot even before
# any differential schedule references it, so future chains can use it.
return "$incremental_backups_dir/$id/$d->{'id'}";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't be done safely for existing backups, as it would cause the incremental state file to change on Virtualmin upgrade. That's why I did it the way I did..

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate and explain exactly why "this can't be done safely for existing backups" so I can get a better picture of your view of the problem?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is, with this change the incremental file for a full backup would change EVEN if there is no differential backup specifically referencing it, which we don't want.

Also, what's with this check for $id =~ /^\d+$/ && $id >= 3 ? The scheduled backup ID doesn't mean anything, except for legacy ID 1. There would never ben an ID 0.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood now! Thanks for pointing this out! I have fixed it in this 5635bcb change. Please check.

Comment thread backups-lib.pl Outdated
foreach my $lockfile (@lockfiles) {
&unlock_file($lockfile);
}
# Release lock on dest file

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this indentation change?

Comment thread backups-lib.pl Outdated
Comment thread backups-lib.pl
@jcameron
jcameron merged commit e483e28 into master Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants