Add and populate mod time for BaseModel (#4065)

Get the last time a model was modified and return it in BaseModel. This will help with discovering what items can be garbage collected during incomplete backup cleanup as we don't want to accidentally delete in-flight backups.

---

#### 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

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

#### Issue(s)

* #3217

#### Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-08-21 16:48:57 -07:00 committed by GitHub
parent 5808797fc6
commit 99edf7d5b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 10 deletions

View File

@ -210,6 +210,7 @@ func (ms ModelStore) populateBaseModelFromMetadata(
base.ID = model.StableID(id)
base.ModelVersion = v
base.Tags = m.Labels
base.ModTime = m.ModTime
stripHiddenTags(base.Tags)

View File

@ -4,6 +4,7 @@ import (
"context"
"sync"
"testing"
"time"
"github.com/alcionai/clues"
"github.com/google/uuid"
@ -34,6 +35,18 @@ func getModelStore(t *testing.T, ctx context.Context) *ModelStore {
return &ModelStore{c: c, modelVersion: globalModelVersion}
}
func assertEqualNoModTime(t *testing.T, expected, got *fooModel) {
t.Helper()
expectedClean := *expected
gotClean := *got
expectedClean.ModTime = time.Time{}
gotClean.ModTime = time.Time{}
assert.Equal(t, expectedClean, gotClean)
}
// ---------------
// unit tests
// ---------------
@ -259,6 +272,8 @@ func (suite *ModelStoreIntegrationSuite) TestPutGet() {
// Avoid some silly test errors from comparing nil to empty map.
foo.Tags = map[string]string{}
startTime := time.Now()
err := suite.m.Put(suite.ctx, test.s, foo)
test.check(t, err, clues.ToCore(err))
@ -273,11 +288,17 @@ func (suite *ModelStoreIntegrationSuite) TestPutGet() {
returned := &fooModel{}
err = suite.m.Get(suite.ctx, test.s, foo.ID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
assert.WithinDuration(t, startTime, returned.ModTime, 5*time.Second)
returned = &fooModel{}
err = suite.m.GetWithModelStoreID(suite.ctx, test.s, foo.ModelStoreID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
assert.WithinDuration(t, startTime, returned.ModTime, 5*time.Second)
})
}
}
@ -324,11 +345,11 @@ func (suite *ModelStoreIntegrationSuite) TestPutGet_PreSetID() {
err = suite.m.Get(suite.ctx, mdl, foo.ID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
err = suite.m.GetWithModelStoreID(suite.ctx, mdl, foo.ModelStoreID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
})
}
}
@ -350,11 +371,11 @@ func (suite *ModelStoreIntegrationSuite) TestPutGet_WithTags() {
returned := &fooModel{}
err = suite.m.Get(suite.ctx, theModelType, foo.ID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
err = suite.m.GetWithModelStoreID(suite.ctx, theModelType, foo.ModelStoreID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
}
func (suite *ModelStoreIntegrationSuite) TestGet_NotFoundErrors() {
@ -559,7 +580,16 @@ func (suite *ModelStoreIntegrationSuite) TestGetOfTypeWithTags() {
ids, err := suite.m.GetIDsForType(suite.ctx, test.s, test.tags)
require.NoError(t, err, clues.ToCore(err))
assert.ElementsMatch(t, expected, ids)
cleanIDs := make([]*model.BaseModel, 0, len(ids))
for _, id := range ids {
id2 := *id
id2.ModTime = time.Time{}
cleanIDs = append(cleanIDs, &id2)
}
assert.ElementsMatch(t, expected, cleanIDs)
})
}
}
@ -627,7 +657,7 @@ func (suite *ModelStoreIntegrationSuite) TestPutUpdate() {
err = m.GetWithModelStoreID(ctx, theModelType, foo.ModelStoreID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
ids, err := m.GetIDsForType(ctx, theModelType, nil)
require.NoError(t, err, clues.ToCore(err))
@ -822,7 +852,7 @@ func (suite *ModelStoreRegressionSuite) TestFailDuringWriteSessionHasNoVisibleEf
err = m.GetWithModelStoreID(ctx, theModelType, foo.ModelStoreID, returned)
require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, foo, returned)
assertEqualNoModTime(t, foo, returned)
}
func openConnAndModelStore(

View File

@ -1,6 +1,8 @@
package model
import (
"time"
"github.com/kopia/kopia/repo/manifest"
)
@ -69,6 +71,7 @@ type BaseModel struct {
// the struct are not serialized directly into the stored model, but are part
// of the metadata for the model.
Tags map[string]string `json:"-"`
ModTime time.Time `json:"-"`
}
func (bm *BaseModel) Base() *BaseModel {