diff --git a/src/internal/kopia/backup_bases.go b/src/internal/kopia/backup_bases.go index 4c2c95fb7..199640ea4 100644 --- a/src/internal/kopia/backup_bases.go +++ b/src/internal/kopia/backup_bases.go @@ -17,12 +17,12 @@ import ( type BackupBases interface { RemoveMergeBaseByManifestID(manifestID manifest.ID) Backups() []BackupEntry - AssistBackups() []BackupEntry + UniqueAssistBackups() []BackupEntry MinBackupVersion() int MergeBases() []ManifestEntry - ClearMergeBases() - AssistBases() []ManifestEntry - ClearAssistBases() + DisableMergeBases() + UniqueAssistBases() []ManifestEntry + DisableAssistBases() MergeBackupBases( ctx context.Context, other BackupBases, @@ -37,6 +37,13 @@ type backupBases struct { mergeBases []ManifestEntry assistBackups []BackupEntry assistBases []ManifestEntry + + // disableAssistBases denote whether any assist bases should be returned to + // kopia during snapshot operation. + disableAssistBases bool + // disableMergeBases denotes whether any bases should be returned from calls + // to MergeBases(). + disableMergeBases bool } func (bb *backupBases) RemoveMergeBaseByManifestID(manifestID manifest.ID) { @@ -71,10 +78,18 @@ func (bb *backupBases) RemoveMergeBaseByManifestID(manifestID manifest.ID) { } func (bb backupBases) Backups() []BackupEntry { + if bb.disableMergeBases { + return nil + } + return slices.Clone(bb.backups) } -func (bb backupBases) AssistBackups() []BackupEntry { +func (bb backupBases) UniqueAssistBackups() []BackupEntry { + if bb.disableAssistBases { + return nil + } + return slices.Clone(bb.assistBackups) } @@ -95,20 +110,27 @@ func (bb *backupBases) MinBackupVersion() int { } func (bb backupBases) MergeBases() []ManifestEntry { + if bb.disableMergeBases { + return nil + } + return slices.Clone(bb.mergeBases) } -func (bb *backupBases) ClearMergeBases() { - bb.mergeBases = nil - bb.backups = nil +func (bb *backupBases) DisableMergeBases() { + bb.disableMergeBases = true } -func (bb backupBases) AssistBases() []ManifestEntry { +func (bb backupBases) UniqueAssistBases() []ManifestEntry { + if bb.disableAssistBases { + return nil + } + return slices.Clone(bb.assistBases) } -func (bb *backupBases) ClearAssistBases() { - bb.assistBases = nil +func (bb *backupBases) DisableAssistBases() { + bb.disableAssistBases = true } // MergeBackupBases reduces the two BackupBases into a single BackupBase. @@ -116,6 +138,9 @@ func (bb *backupBases) ClearAssistBases() { // some migration that disrupts lookup), and that the BackupBases used to call // this function contains the current version. // +// This call should be made prior to Disable*Bases being called on either the +// called BackupBases or the passed in BackupBases. +// // reasonToKey should be a function that, given a Reasoner, will produce some // string that represents Reasoner in the context of the merge operation. For // example, to merge BackupBases across a ProtectedResource migration, the @@ -134,11 +159,11 @@ func (bb *backupBases) MergeBackupBases( other BackupBases, reasonToKey func(reason identity.Reasoner) string, ) BackupBases { - if other == nil || (len(other.MergeBases()) == 0 && len(other.AssistBases()) == 0) { + if other == nil || (len(other.MergeBases()) == 0 && len(other.UniqueAssistBases()) == 0) { return bb } - if bb == nil || (len(bb.MergeBases()) == 0 && len(bb.AssistBases()) == 0) { + if bb == nil || (len(bb.MergeBases()) == 0 && len(bb.UniqueAssistBases()) == 0) { return other } @@ -189,11 +214,11 @@ func (bb *backupBases) MergeBackupBases( res := &backupBases{ backups: bb.Backups(), mergeBases: bb.MergeBases(), - assistBases: bb.AssistBases(), + assistBases: bb.UniqueAssistBases(), // Note that assistBackups are a new feature and don't exist // in prior versions where we were using UPN based reasons i.e. // other won't have any assistBackups. - assistBackups: bb.AssistBackups(), + assistBackups: bb.UniqueAssistBackups(), } // Add new mergeBases and backups. diff --git a/src/internal/kopia/backup_bases_test.go b/src/internal/kopia/backup_bases_test.go index faa402162..5d450dd81 100644 --- a/src/internal/kopia/backup_bases_test.go +++ b/src/internal/kopia/backup_bases_test.go @@ -185,22 +185,40 @@ func (suite *BackupBasesUnitSuite) TestRemoveMergeBaseByManifestID() { } } -func (suite *BackupBasesUnitSuite) TestClearMergeBases() { +func (suite *BackupBasesUnitSuite) TestDisableMergeBases() { + t := suite.T() bb := &backupBases{ - backups: make([]BackupEntry, 2), - mergeBases: make([]ManifestEntry, 2), + backups: make([]BackupEntry, 2), + mergeBases: make([]ManifestEntry, 2), + assistBackups: make([]BackupEntry, 2), + assistBases: make([]ManifestEntry, 2), } - bb.ClearMergeBases() - assert.Empty(suite.T(), bb.Backups()) - assert.Empty(suite.T(), bb.MergeBases()) + bb.DisableMergeBases() + assert.Empty(t, bb.Backups()) + assert.Empty(t, bb.MergeBases()) + + // Assist base set should be unchanged. + assert.Len(t, bb.UniqueAssistBackups(), 2) + assert.Len(t, bb.UniqueAssistBases(), 2) } -func (suite *BackupBasesUnitSuite) TestClearAssistBases() { - bb := &backupBases{assistBases: make([]ManifestEntry, 2)} +func (suite *BackupBasesUnitSuite) TestDisableAssistBases() { + t := suite.T() + bb := &backupBases{ + backups: make([]BackupEntry, 2), + mergeBases: make([]ManifestEntry, 2), + assistBases: make([]ManifestEntry, 2), + assistBackups: make([]BackupEntry, 2), + } - bb.ClearAssistBases() - assert.Empty(suite.T(), bb.AssistBases()) + bb.DisableAssistBases() + assert.Empty(t, bb.UniqueAssistBases()) + assert.Empty(t, bb.UniqueAssistBackups()) + + // Merge base should be unchanged. + assert.Len(t, bb.Backups(), 2) + assert.Len(t, bb.MergeBases(), 2) } func (suite *BackupBasesUnitSuite) TestMergeBackupBases() { diff --git a/src/internal/kopia/base_finder_test.go b/src/internal/kopia/base_finder_test.go index 3cacb9adf..391739e7c 100644 --- a/src/internal/kopia/base_finder_test.go +++ b/src/internal/kopia/base_finder_test.go @@ -299,7 +299,7 @@ func (suite *BaseFinderUnitSuite) TestNoResult_NoBackupsOrSnapshots() { bb := bf.FindBases(ctx, reasons, nil) assert.Empty(t, bb.MergeBases()) - assert.Empty(t, bb.AssistBases()) + assert.Empty(t, bb.UniqueAssistBases()) } func (suite *BaseFinderUnitSuite) TestNoResult_ErrorListingSnapshots() { @@ -318,7 +318,7 @@ func (suite *BaseFinderUnitSuite) TestNoResult_ErrorListingSnapshots() { bb := bf.FindBases(ctx, reasons, nil) assert.Empty(t, bb.MergeBases()) - assert.Empty(t, bb.AssistBases()) + assert.Empty(t, bb.UniqueAssistBases()) } func (suite *BaseFinderUnitSuite) TestGetBases() { @@ -1061,7 +1061,7 @@ func (suite *BaseFinderUnitSuite) TestGetBases() { test.expectedBaseReasons) checkBackupEntriesMatch( t, - bb.AssistBackups(), + bb.UniqueAssistBackups(), test.backupData, test.expectedAssistReasons) @@ -1072,7 +1072,7 @@ func (suite *BaseFinderUnitSuite) TestGetBases() { test.expectedBaseReasons) checkManifestEntriesMatch( t, - bb.AssistBases(), + bb.UniqueAssistBases(), test.manifestData, test.expectedAssistManifestReasons) }) diff --git a/src/internal/kopia/mock_backup_base.go b/src/internal/kopia/mock_backup_base.go index 7edea1c39..428478a96 100644 --- a/src/internal/kopia/mock_backup_base.go +++ b/src/internal/kopia/mock_backup_base.go @@ -14,17 +14,17 @@ func AssertBackupBasesEqual(t *testing.T, expect, got BackupBases) { if expect == nil { assert.Empty(t, got.Backups(), "backups") assert.Empty(t, got.MergeBases(), "merge bases") - assert.Empty(t, got.AssistBackups(), "assist backups") - assert.Empty(t, got.AssistBases(), "assist bases") + assert.Empty(t, got.UniqueAssistBackups(), "assist backups") + assert.Empty(t, got.UniqueAssistBases(), "assist bases") return } if got == nil { - if len(expect.Backups()) > 0 && - len(expect.MergeBases()) > 0 && - len(expect.AssistBackups()) > 0 && - len(expect.AssistBases()) > 0 { + if len(expect.Backups()) > 0 || + len(expect.MergeBases()) > 0 || + len(expect.UniqueAssistBackups()) > 0 || + len(expect.UniqueAssistBases()) > 0 { assert.Fail(t, "got was nil but expected non-nil result %v", expect) } @@ -33,8 +33,8 @@ func AssertBackupBasesEqual(t *testing.T, expect, got BackupBases) { assert.ElementsMatch(t, expect.Backups(), got.Backups(), "backups") assert.ElementsMatch(t, expect.MergeBases(), got.MergeBases(), "merge bases") - assert.ElementsMatch(t, expect.AssistBackups(), got.AssistBackups(), "assist backups") - assert.ElementsMatch(t, expect.AssistBases(), got.AssistBases(), "assist bases") + assert.ElementsMatch(t, expect.UniqueAssistBackups(), got.UniqueAssistBackups(), "assist backups") + assert.ElementsMatch(t, expect.UniqueAssistBases(), got.UniqueAssistBases(), "assist bases") } func NewMockBackupBases() *MockBackupBases { @@ -52,22 +52,22 @@ func (bb *MockBackupBases) WithBackups(b ...BackupEntry) *MockBackupBases { func (bb *MockBackupBases) WithMergeBases(m ...ManifestEntry) *MockBackupBases { bb.backupBases.mergeBases = append(bb.MergeBases(), m...) - bb.backupBases.assistBases = append(bb.AssistBases(), m...) + bb.backupBases.assistBases = append(bb.UniqueAssistBases(), m...) return bb } func (bb *MockBackupBases) WithAssistBackups(b ...BackupEntry) *MockBackupBases { - bb.backupBases.assistBackups = append(bb.AssistBackups(), b...) + bb.backupBases.assistBackups = append(bb.UniqueAssistBackups(), b...) return bb } func (bb *MockBackupBases) WithAssistBases(m ...ManifestEntry) *MockBackupBases { - bb.backupBases.assistBases = append(bb.AssistBases(), m...) + bb.backupBases.assistBases = append(bb.UniqueAssistBases(), m...) return bb } func (bb *MockBackupBases) ClearMockAssistBases() *MockBackupBases { - bb.backupBases.ClearAssistBases() + bb.backupBases.DisableAssistBases() return bb } diff --git a/src/internal/kopia/wrapper.go b/src/internal/kopia/wrapper.go index 2b6874be2..ede8b7c0b 100644 --- a/src/internal/kopia/wrapper.go +++ b/src/internal/kopia/wrapper.go @@ -178,7 +178,7 @@ func (w Wrapper) ConsumeBackupCollections( mergeBase = bases.MergeBases() } - assistBase = bases.AssistBases() + assistBase = bases.UniqueAssistBases() } dirTree, err := inflateDirTree( diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 54f321472..2824c70da 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -732,7 +732,7 @@ func mergeDetails( // leaves us in a bit of a pickle if the user has run any concurrent backups // with overlapping Reasons that turn into assist bases, but the modTime check // in DetailsMergeInfoer should handle that. - for _, base := range bases.AssistBackups() { + for _, base := range bases.UniqueAssistBackups() { added, err := mergeItemsFromBase( ctx, false, diff --git a/src/internal/operations/manifests.go b/src/internal/operations/manifests.go index 2c16d1ca7..c64631d8e 100644 --- a/src/internal/operations/manifests.go +++ b/src/internal/operations/manifests.go @@ -39,13 +39,13 @@ func produceManifestsAndMetadata( if !useMergeBases || !getMetadata { logger.Ctx(ctx).Debug("full backup requested, dropping merge bases") - bb.ClearMergeBases() + bb.DisableMergeBases() } if dropAssistBases { logger.Ctx(ctx).Debug("no caching requested, dropping assist bases") - bb.ClearAssistBases() + bb.DisableAssistBases() } return bb, meta, useMergeBases, nil