Create generic items without Info() (#4365)

Create generic Item implementations that
don't implement the ItemInfo interface.
These implementations can be used for
things like metadata files.


---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)

* #4191

#### Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-09-28 20:29:15 -07:00 committed by GitHub
parent d5cdf37369
commit 5521177aee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 117 additions and 28 deletions

View File

@ -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
}

View File

@ -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()