diff --git a/src/internal/data/item.go b/src/internal/data/item.go index 862699a5d..6d316ad6b 100644 --- a/src/internal/data/item.go +++ b/src/internal/data/item.go @@ -16,16 +16,23 @@ import ( ) var ( + _ Item = &unindexedPrefetchedItem{} + _ ItemModTime = &unindexedPrefetchedItem{} + _ Item = &prefetchedItem{} _ ItemInfo = &prefetchedItem{} _ ItemModTime = &prefetchedItem{} + + _ Item = &unindexedLazyItem{} + _ ItemModTime = &unindexedLazyItem{} + _ Item = &lazyItem{} _ ItemInfo = &lazyItem{} _ ItemModTime = &lazyItem{} ) func NewDeletedItem(itemID string) Item { - return &prefetchedItem{ + return &unindexedPrefetchedItem{ id: itemID, deleted: true, // TODO(ashmrtn): This really doesn't need to be set since deleted items are @@ -35,24 +42,26 @@ func NewDeletedItem(itemID string) Item { } } -func NewPrefetchedItem( +func NewUnindexedPrefetchedItem( reader io.ReadCloser, itemID string, - info details.ItemInfo, + modTime time.Time, ) Item { - return &prefetchedItem{ + return &unindexedPrefetchedItem{ id: itemID, reader: reader, - info: info, - modTime: info.Modified(), + modTime: modTime, } } -// prefetchedItem represents a single item retrieved from the remote service. -type prefetchedItem struct { +// unindexedPrefetchedItem represents a single item retrieved from the remote +// service. +// +// This item doesn't implement ItemInfo so it's safe to use for items like +// metadata that shouldn't appear in backup details. +type unindexedPrefetchedItem struct { id string reader io.ReadCloser - info details.ItemInfo // modTime is the modified time of the item. It should match the modTime in // info if info is present. Here as a separate field so that deleted items // don't error out by trying to source it from info. @@ -63,26 +72,50 @@ type prefetchedItem struct { deleted bool } -func (i prefetchedItem) ID() string { +func (i unindexedPrefetchedItem) ID() string { return i.id } -func (i *prefetchedItem) ToReader() io.ReadCloser { +func (i *unindexedPrefetchedItem) ToReader() io.ReadCloser { return i.reader } -func (i prefetchedItem) Deleted() bool { +func (i unindexedPrefetchedItem) Deleted() bool { return i.deleted } +func (i unindexedPrefetchedItem) ModTime() time.Time { + return i.modTime +} + +func NewPrefetchedItem( + reader io.ReadCloser, + itemID string, + info details.ItemInfo, +) Item { + return &prefetchedItem{ + unindexedPrefetchedItem: unindexedPrefetchedItem{ + id: itemID, + reader: reader, + modTime: info.Modified(), + }, + info: info, + } +} + +// prefetchedItem represents a single item retrieved from the remote service. +// +// This item implements ItemInfo so it should be used for things that need to +// appear in backup details. +type prefetchedItem struct { + unindexedPrefetchedItem + info details.ItemInfo +} + func (i prefetchedItem) Info() (details.ItemInfo, error) { return i.info, nil } -func (i prefetchedItem) ModTime() time.Time { - return i.modTime -} - type ItemDataGetter interface { GetData( context.Context, @@ -90,14 +123,14 @@ type ItemDataGetter interface { ) (io.ReadCloser, *details.ItemInfo, bool, error) } -func NewLazyItem( +func NewUnindexedLazyItem( ctx context.Context, itemGetter ItemDataGetter, itemID string, modTime time.Time, errs *fault.Bus, ) Item { - return &lazyItem{ + return &unindexedLazyItem{ ctx: ctx, id: itemID, itemGetter: itemGetter, @@ -106,10 +139,13 @@ func NewLazyItem( } } -// lazyItem represents a single item retrieved from the remote service. It -// lazily fetches the item's data when the first call to ToReader().Read() is +// unindexedLazyItem represents a single item retrieved from the remote service. +// It lazily fetches the item's data when the first call to ToReader().Read() is // made. -type lazyItem struct { +// +// This item doesn't implement ItemInfo so it's safe to use for items like +// metadata that shouldn't appear in backup details. +type unindexedLazyItem struct { ctx context.Context mu sync.Mutex id string @@ -129,11 +165,11 @@ type lazyItem struct { delInFlight bool } -func (i *lazyItem) ID() string { +func (i *unindexedLazyItem) ID() string { return i.id } -func (i *lazyItem) ToReader() io.ReadCloser { +func (i *unindexedLazyItem) ToReader() io.ReadCloser { return lazy.NewLazyReadCloser(func() (io.ReadCloser, error) { // Don't allow getting Item info while trying to initialize said info. // GetData could be a long running call, but in theory nothing should happen @@ -167,10 +203,42 @@ func (i *lazyItem) ToReader() io.ReadCloser { }) } -func (i *lazyItem) Deleted() bool { +func (i *unindexedLazyItem) Deleted() bool { return false } +func (i *unindexedLazyItem) ModTime() time.Time { + return i.modTime +} + +func NewLazyItem( + ctx context.Context, + itemGetter ItemDataGetter, + itemID string, + modTime time.Time, + errs *fault.Bus, +) Item { + return &lazyItem{ + unindexedLazyItem: unindexedLazyItem{ + ctx: ctx, + id: itemID, + itemGetter: itemGetter, + modTime: modTime, + errs: errs, + }, + } +} + +// lazyItem represents a single item retrieved from the remote service. It +// lazily fetches the item's data when the first call to ToReader().Read() is +// made. +// +// This item implements ItemInfo so it should be used for things that need to +// appear in backup details. +type lazyItem struct { + unindexedLazyItem +} + func (i *lazyItem) Info() (details.ItemInfo, error) { i.mu.Lock() defer i.mu.Unlock() @@ -184,7 +252,3 @@ func (i *lazyItem) Info() (details.ItemInfo, error) { return *i.info, nil } - -func (i *lazyItem) ModTime() time.Time { - return i.modTime -} diff --git a/src/internal/data/item_test.go b/src/internal/data/item_test.go index 864e70890..9484613e4 100644 --- a/src/internal/data/item_test.go +++ b/src/internal/data/item_test.go @@ -49,6 +49,31 @@ func TestItemUnitSuite(t *testing.T) { suite.Run(t, &ItemUnitSuite{Suite: tester.NewUnitSuite(t)}) } +func (suite *ItemUnitSuite) TestUnindexedPrefetchedItem() { + prefetch := data.NewUnindexedPrefetchedItem( + io.NopCloser(bytes.NewReader([]byte{})), + "foo", + time.Time{}) + _, ok := prefetch.(data.ItemInfo) + assert.False(suite.T(), ok, "unindexedPrefetchedItem implements Info()") +} + +func (suite *ItemUnitSuite) TestUnindexedLazyItem() { + t := suite.T() + + ctx, flush := tester.NewContext(t) + defer flush() + + lazy := data.NewUnindexedLazyItem( + ctx, + nil, + "foo", + time.Time{}, + fault.New(true)) + _, ok := lazy.(data.ItemInfo) + assert.False(t, ok, "unindexedLazyItem implements Info()") +} + func (suite *ItemUnitSuite) TestDeletedItem() { var ( t = suite.T()