fix overlapping bases after fallback union
In a case where the current manifests and the fallback contain both distinct and overlapping categories, the end result contains multiple bases for the same category. Verifydistinctresons didn't catch this because it includes the resource owner.
This commit is contained in:
parent
9e692c7e2e
commit
0e3469c23e
@ -2,6 +2,7 @@ package operations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/kopia/kopia/repo/manifest"
|
||||
@ -93,20 +94,6 @@ func produceManifestsAndMetadata(
|
||||
return ms, nil, false, nil
|
||||
}
|
||||
|
||||
// We only need to check that we have 1:1 reason:base if we're doing an
|
||||
// incremental with associated metadata. This ensures that we're only sourcing
|
||||
// data from a single Point-In-Time (base) for each incremental backup.
|
||||
//
|
||||
// TODO(ashmrtn): This may need updating if we start sourcing item backup
|
||||
// details from previous snapshots when using kopia-assisted incrementals.
|
||||
if err := verifyDistinctBases(ctx, ms); err != nil {
|
||||
logger.Ctx(ctx).With("error", err).Infow(
|
||||
"unioned snapshot collision, falling back to full backup",
|
||||
clues.In(ctx).Slice()...)
|
||||
|
||||
return ms, nil, false, nil
|
||||
}
|
||||
|
||||
for _, man := range ms {
|
||||
if len(man.IncompleteReason) > 0 {
|
||||
continue
|
||||
@ -163,6 +150,7 @@ func produceManifestsAndMetadata(
|
||||
LogFaultErrors(ctx, fb.Errors(), "collecting metadata")
|
||||
|
||||
if err != nil && !errors.Is(err, data.ErrNotFound) {
|
||||
fmt.Printf("\n-----\nCOLLECTING METADATA %+v\n-----\n", err)
|
||||
// prior metadata isn't guaranteed to exist.
|
||||
// if it doesn't, we'll just have to do a
|
||||
// full backup for that data.
|
||||
@ -234,6 +222,8 @@ func unionManifests(
|
||||
|
||||
// backfill from the fallback where necessary
|
||||
for _, m := range fallback {
|
||||
useReasons := []kopia.Reason{}
|
||||
|
||||
for _, r := range m.Reasons {
|
||||
k := r.Service.String() + r.Category.String()
|
||||
t := tups[k]
|
||||
@ -245,6 +235,8 @@ func unionManifests(
|
||||
continue
|
||||
}
|
||||
|
||||
useReasons = append(useReasons, r)
|
||||
|
||||
if len(m.IncompleteReason) > 0 && t.incomplete == nil {
|
||||
t.incomplete = m
|
||||
} else if len(m.IncompleteReason) == 0 {
|
||||
@ -253,6 +245,10 @@ func unionManifests(
|
||||
|
||||
tups[k] = t
|
||||
}
|
||||
|
||||
if len(m.IncompleteReason) == 0 && len(useReasons) > 0 {
|
||||
m.Reasons = useReasons
|
||||
}
|
||||
}
|
||||
|
||||
// collect the results into a single slice of manifests
|
||||
|
||||
@ -757,18 +757,20 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
main []testInput
|
||||
man []testInput
|
||||
fallback []testInput
|
||||
reasons []kopia.Reason
|
||||
fallbackReasons []kopia.Reason
|
||||
categories []path.CategoryType
|
||||
manCategories []path.CategoryType
|
||||
fbCategories []path.CategoryType
|
||||
assertErr assert.ErrorAssertionFunc
|
||||
expectManIDs []string
|
||||
expectNilMans bool
|
||||
expectReasons map[string][]path.CategoryType
|
||||
}{
|
||||
{
|
||||
name: "only mans, no fallbacks",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
@ -777,8 +779,13 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
incomplete: true,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete, manIncomplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete, manIncomplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.EmailCategory},
|
||||
manIncomplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no mans, only fallbacks",
|
||||
@ -791,12 +798,17 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
incomplete: true,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{fbComplete, fbIncomplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{fbComplete, fbIncomplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
fbComplete: {path.EmailCategory},
|
||||
fbIncomplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complete mans and fallbacks",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
@ -806,12 +818,16 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
id: fbComplete,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "incomplete mans and fallbacks",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manIncomplete,
|
||||
incomplete: true,
|
||||
@ -823,12 +839,16 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
incomplete: true,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manIncomplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manIncomplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manIncomplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complete and incomplete mans and fallbacks",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
@ -846,12 +866,17 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
incomplete: true,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete, manIncomplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete, manIncomplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.EmailCategory},
|
||||
manIncomplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "incomplete mans, complete fallbacks",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manIncomplete,
|
||||
incomplete: true,
|
||||
@ -862,12 +887,17 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
id: fbComplete,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{fbComplete, manIncomplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{fbComplete, manIncomplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
fbComplete: {path.EmailCategory},
|
||||
manIncomplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complete mans, incomplete fallbacks",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
@ -878,12 +908,16 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
incomplete: true,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complete mans, complete fallbacks, multiple reasons",
|
||||
main: []testInput{
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
@ -893,8 +927,52 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
id: fbComplete,
|
||||
},
|
||||
},
|
||||
categories: []path.CategoryType{path.EmailCategory, path.ContactsCategory},
|
||||
expectManIDs: []string{manComplete},
|
||||
manCategories: []path.CategoryType{path.EmailCategory, path.ContactsCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory, path.ContactsCategory},
|
||||
expectManIDs: []string{manComplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.EmailCategory, path.ContactsCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complete mans, complete fallbacks, distinct reasons",
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
},
|
||||
fallback: []testInput{
|
||||
{
|
||||
id: fbComplete,
|
||||
},
|
||||
},
|
||||
manCategories: []path.CategoryType{path.ContactsCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory},
|
||||
expectManIDs: []string{manComplete, fbComplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.ContactsCategory},
|
||||
fbComplete: {path.EmailCategory},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fb has superset of mans reasons",
|
||||
man: []testInput{
|
||||
{
|
||||
id: manComplete,
|
||||
},
|
||||
},
|
||||
fallback: []testInput{
|
||||
{
|
||||
id: fbComplete,
|
||||
},
|
||||
},
|
||||
manCategories: []path.CategoryType{path.ContactsCategory},
|
||||
fbCategories: []path.CategoryType{path.EmailCategory, path.ContactsCategory, path.EventsCategory},
|
||||
expectManIDs: []string{manComplete, fbComplete},
|
||||
expectReasons: map[string][]path.CategoryType{
|
||||
manComplete: {path.ContactsCategory},
|
||||
fbComplete: {path.EmailCategory, path.EventsCategory},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
@ -907,7 +985,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
mainReasons := []kopia.Reason{}
|
||||
fbReasons := []kopia.Reason{}
|
||||
|
||||
for _, cat := range test.categories {
|
||||
for _, cat := range test.manCategories {
|
||||
mainReasons = append(
|
||||
mainReasons,
|
||||
kopia.Reason{
|
||||
@ -915,7 +993,9 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
Service: path.ExchangeService,
|
||||
Category: cat,
|
||||
})
|
||||
}
|
||||
|
||||
for _, cat := range test.fbCategories {
|
||||
fbReasons = append(
|
||||
fbReasons,
|
||||
kopia.Reason{
|
||||
@ -927,7 +1007,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
|
||||
mans := []*kopia.ManifestEntry{}
|
||||
|
||||
for _, m := range test.main {
|
||||
for _, m := range test.man {
|
||||
incomplete := ""
|
||||
if m.incomplete {
|
||||
incomplete = "ir"
|
||||
@ -959,8 +1039,19 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata_fallb
|
||||
assert.False(t, b, "no-metadata is forced for this test")
|
||||
|
||||
manIDs := []string{}
|
||||
|
||||
for _, m := range mans {
|
||||
manIDs = append(manIDs, string(m.ID))
|
||||
|
||||
reasons, ok := test.expectReasons[string(m.ID)]
|
||||
assert.True(t, ok, "unexpected manifest in result: ", m.ID)
|
||||
|
||||
mrs := []path.CategoryType{}
|
||||
for _, r := range m.Reasons {
|
||||
mrs = append(mrs, r.Category)
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t, reasons, mrs)
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t, test.expectManIDs, manIDs)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user