diff --git a/database/boilerplate_test.go b/database/boilerplate_test.go index c2a717e..9cf3383 100644 --- a/database/boilerplate_test.go +++ b/database/boilerplate_test.go @@ -16,7 +16,10 @@ type Example struct { } var ( - exampleDB = NewInterface(nil) + exampleDB = NewInterface(&Options{ + Internal: true, + Local: true, + }) ) // GetExample gets an Example from the database. diff --git a/database/controller.go b/database/controller.go index e5e94fc..4f7ae3e 100644 --- a/database/controller.go +++ b/database/controller.go @@ -1,8 +1,10 @@ package database import ( + "context" "errors" "sync" + "time" "github.com/tevino/abool" @@ -226,7 +228,7 @@ func (c *Controller) readUnlockerAfterQuery(it *iterator.Iterator) { } // Maintain runs the Maintain method on the storage. -func (c *Controller) Maintain() error { +func (c *Controller) Maintain(ctx context.Context) error { c.writeLock.RLock() defer c.writeLock.RUnlock() @@ -234,11 +236,11 @@ func (c *Controller) Maintain() error { return nil } - return c.storage.Maintain() + return c.storage.Maintain(ctx) } // MaintainThorough runs the MaintainThorough method on the storage. -func (c *Controller) MaintainThorough() error { +func (c *Controller) MaintainThorough(ctx context.Context) error { c.writeLock.RLock() defer c.writeLock.RUnlock() @@ -246,7 +248,19 @@ func (c *Controller) MaintainThorough() error { return nil } - return c.storage.MaintainThorough() + return c.storage.MaintainThorough(ctx) +} + +// MaintainRecordStates runs the record state lifecycle maintenance on the storage. +func (c *Controller) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { + c.writeLock.RLock() + defer c.writeLock.RUnlock() + + if shuttingDown.IsSet() { + return nil + } + + return c.storage.MaintainRecordStates(ctx, purgeDeletedBefore) } // Shutdown shuts down the storage. diff --git a/database/database_test.go b/database/database_test.go index 8d20a0f..5749cec 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -11,9 +11,11 @@ import ( "testing" "time" - "github.com/safing/portbase/database/record" + "github.com/safing/portbase/database/storage" q "github.com/safing/portbase/database/query" + "github.com/safing/portbase/database/record" + _ "github.com/safing/portbase/database/storage/badger" _ "github.com/safing/portbase/database/storage/bbolt" _ "github.com/safing/portbase/database/storage/fstree" @@ -24,117 +26,195 @@ func makeKey(dbName, key string) string { return fmt.Sprintf("%s:%s", dbName, key) } -func testDatabase(t *testing.T, storageType string) { - dbName := fmt.Sprintf("testing-%s", storageType) - _, err := Register(&Database{ - Name: dbName, - Description: fmt.Sprintf("Unit Test Database for %s", storageType), - StorageType: storageType, - PrimaryAPI: "", - }) - if err != nil { - t.Fatal(err) - } +func testDatabase(t *testing.T, storageType string, testPutMany, testRecordMaintenance bool) { //nolint:gocognit,gocyclo + t.Run(fmt.Sprintf("TestStorage_%s", storageType), func(t *testing.T) { + dbName := fmt.Sprintf("testing-%s", storageType) + fmt.Println(dbName) + _, err := Register(&Database{ + Name: dbName, + Description: fmt.Sprintf("Unit Test Database for %s", storageType), + StorageType: storageType, + PrimaryAPI: "", + }) + if err != nil { + t.Fatal(err) + } + dbController, err := getController(dbName) + if err != nil { + t.Fatal(err) + } - // hook - hook, err := RegisterHook(q.New(dbName).MustBeValid(), &HookBase{}) - if err != nil { - t.Fatal(err) - } + // hook + hook, err := RegisterHook(q.New(dbName).MustBeValid(), &HookBase{}) + if err != nil { + t.Fatal(err) + } - // interface - db := NewInterface(&Options{ - Local: true, - Internal: true, - }) + // interface + db := NewInterface(&Options{ + Local: true, + Internal: true, + }) - // sub - sub, err := db.Subscribe(q.New(dbName).MustBeValid()) - if err != nil { - t.Fatal(err) - } + // sub + sub, err := db.Subscribe(q.New(dbName).MustBeValid()) + if err != nil { + t.Fatal(err) + } - A := NewExample(makeKey(dbName, "A"), "Herbert", 411) - err = A.Save() - if err != nil { - t.Fatal(err) - } + A := NewExample(dbName+":A", "Herbert", 411) + err = A.Save() + if err != nil { + t.Fatal(err) + } - B := NewExample(makeKey(dbName, "B"), "Fritz", 347) - err = B.Save() - if err != nil { - t.Fatal(err) - } + B := NewExample(makeKey(dbName, "B"), "Fritz", 347) + err = B.Save() + if err != nil { + t.Fatal(err) + } - C := NewExample(makeKey(dbName, "C"), "Norbert", 217) - err = C.Save() - if err != nil { - t.Fatal(err) - } + C := NewExample(makeKey(dbName, "C"), "Norbert", 217) + err = C.Save() + if err != nil { + t.Fatal(err) + } - exists, err := db.Exists(makeKey(dbName, "A")) - if err != nil { - t.Fatal(err) - } - if !exists { - t.Fatalf("record %s should exist!", makeKey(dbName, "A")) - } + exists, err := db.Exists(makeKey(dbName, "A")) + if err != nil { + t.Fatal(err) + } + if !exists { + t.Fatalf("record %s should exist!", makeKey(dbName, "A")) + } - A1, err := GetExample(makeKey(dbName, "A")) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(A, A1) { - log.Fatalf("A and A1 mismatch, A1: %v", A1) - } + A1, err := GetExample(makeKey(dbName, "A")) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(A, A1) { + log.Fatalf("A and A1 mismatch, A1: %v", A1) + } - query, err := q.New(dbName).Where( - q.And( - q.Where("Name", q.EndsWith, "bert"), - q.Where("Score", q.GreaterThan, 100), - ), - ).Check() - if err != nil { - t.Fatal(err) - } + cnt := countRecords(t, db, q.New(dbName).Where( + q.And( + q.Where("Name", q.EndsWith, "bert"), + q.Where("Score", q.GreaterThan, 100), + ), + )) + if cnt != 2 { + t.Fatalf("expected two records, got %d", cnt) + } - it, err := db.Query(query) - if err != nil { - t.Fatal(err) - } + // test putmany + if testPutMany { + batchPut := db.PutMany(dbName) + records := []record.Record{A, B, C, nil} // nil is to signify finish + for _, r := range records { + err = batchPut(r) + if err != nil { + t.Fatal(err) + } + } + } - cnt := 0 - for range it.Next { - cnt++ - } - if it.Err() != nil { - t.Fatal(it.Err()) - } - if cnt != 2 { - t.Fatalf("expected two records, got %d", cnt) - } + // test maintenance + if testRecordMaintenance { + now := time.Now().UTC() + nowUnix := now.Unix() - switch storageType { - case "bbolt", "hashmap": - batchPut := db.PutMany(dbName) - records := []record.Record{A, B, C, nil} // nil is to signify finish - for _, r := range records { - err = batchPut(r) + // we start with 3 records without expiry + cnt := countRecords(t, db, q.New(dbName)) + if cnt != 3 { + t.Fatalf("expected three records, got %d", cnt) + } + // delete entry + A.Meta().Deleted = nowUnix - 61 + err = A.Save() if err != nil { t.Fatal(err) } + // expire entry + B.Meta().Expires = nowUnix - 1 + err = B.Save() + if err != nil { + t.Fatal(err) + } + + // one left + cnt = countRecords(t, db, q.New(dbName)) + if cnt != 1 { + t.Fatalf("expected one record, got %d", cnt) + } + + // run maintenance + err = dbController.MaintainRecordStates(context.TODO(), now.Add(-60*time.Second)) + if err != nil { + t.Fatal(err) + } + // one left + cnt = countRecords(t, db, q.New(dbName)) + if cnt != 1 { + t.Fatalf("expected one record, got %d", cnt) + } + + // check status individually + _, err = dbController.storage.Get("A") + if err != storage.ErrNotFound { + t.Errorf("A should be deleted and purged, err=%s", err) + } + B1, err := dbController.storage.Get("B") + if err != nil { + t.Fatalf("should exist: %s, original meta: %+v", err, B.Meta()) + } + if B1.Meta().Deleted == 0 { + t.Errorf("B should be deleted") + } + + // delete last entry + C.Meta().Deleted = nowUnix - 1 + err = C.Save() + if err != nil { + t.Fatal(err) + } + + // run maintenance + err = dbController.MaintainRecordStates(context.TODO(), now) + if err != nil { + t.Fatal(err) + } + + // check status individually + B2, err := dbController.storage.Get("B") + if err == nil { + t.Errorf("B should be deleted and purged, meta: %+v", B2.Meta()) + } else if err != storage.ErrNotFound { + t.Errorf("B should be deleted and purged, err=%s", err) + } + C2, err := dbController.storage.Get("C") + if err == nil { + t.Errorf("C should be deleted and purged, meta: %+v", C2.Meta()) + } else if err != storage.ErrNotFound { + t.Errorf("C should be deleted and purged, err=%s", err) + } + + // none left + cnt = countRecords(t, db, q.New(dbName)) + if cnt != 0 { + t.Fatalf("expected no records, got %d", cnt) + } } - } - err = hook.Cancel() - if err != nil { - t.Fatal(err) - } - err = sub.Cancel() - if err != nil { - t.Fatal(err) - } + err = hook.Cancel() + if err != nil { + t.Fatal(err) + } + err = sub.Cancel() + if err != nil { + t.Fatal(err) + } + }) } func TestDatabaseSystem(t *testing.T) { @@ -158,22 +238,22 @@ func TestDatabaseSystem(t *testing.T) { } defer os.RemoveAll(testDir) // clean up - testDatabase(t, "badger") - testDatabase(t, "bbolt") - testDatabase(t, "fstree") - testDatabase(t, "hashmap") + testDatabase(t, "bbolt", true, true) + testDatabase(t, "hashmap", true, true) + testDatabase(t, "fstree", false, false) + testDatabase(t, "badger", false, false) err = MaintainRecordStates(context.TODO()) if err != nil { t.Fatal(err) } - err = Maintain() + err = Maintain(context.TODO()) if err != nil { t.Fatal(err) } - err = MaintainThorough() + err = MaintainThorough(context.TODO()) if err != nil { t.Fatal(err) } @@ -182,5 +262,25 @@ func TestDatabaseSystem(t *testing.T) { if err != nil { t.Fatal(err) } - +} + +func countRecords(t *testing.T, db *Interface, query *q.Query) int { + _, err := query.Check() + if err != nil { + t.Fatal(err) + } + + it, err := db.Query(query) + if err != nil { + t.Fatal(err) + } + + cnt := 0 + for range it.Next { + cnt++ + } + if it.Err() != nil { + t.Fatal(it.Err()) + } + return cnt } diff --git a/database/dbmodule/maintenance.go b/database/dbmodule/maintenance.go index 0d2a42b..e7e1e14 100644 --- a/database/dbmodule/maintenance.go +++ b/database/dbmodule/maintenance.go @@ -17,12 +17,12 @@ func startMaintenanceTasks() { func maintainBasic(ctx context.Context, task *modules.Task) error { log.Infof("database: running Maintain") - return database.Maintain() + return database.Maintain(ctx) } func maintainThorough(ctx context.Context, task *modules.Task) error { log.Infof("database: running MaintainThorough") - return database.MaintainThorough() + return database.MaintainThorough(ctx) } func maintainRecords(ctx context.Context, task *modules.Task) error { diff --git a/database/interface.go b/database/interface.go index a939960..7e15767 100644 --- a/database/interface.go +++ b/database/interface.go @@ -181,7 +181,7 @@ func (i *Interface) Put(r record.Record) (err error) { return err } } else { - db, err = getController(r.DatabaseKey()) + db, err = getController(r.DatabaseName()) if err != nil { return err } diff --git a/database/maintenance.go b/database/maintenance.go index f517758..19c484f 100644 --- a/database/maintenance.go +++ b/database/maintenance.go @@ -3,20 +3,15 @@ package database import ( "context" "time" - - "github.com/tevino/abool" - - "github.com/safing/portbase/database/query" - "github.com/safing/portbase/database/record" ) // Maintain runs the Maintain method on all storages. -func Maintain() (err error) { +func Maintain(ctx context.Context) (err error) { // copy, as we might use the very long all := duplicateControllers() for _, c := range all { - err = c.Maintain() + err = c.Maintain(ctx) if err != nil { return } @@ -25,12 +20,12 @@ func Maintain() (err error) { } // MaintainThorough runs the MaintainThorough method on all storages. -func MaintainThorough() (err error) { +func MaintainThorough(ctx context.Context) (err error) { // copy, as we might use the very long all := duplicateControllers() for _, c := range all { - err = c.MaintainThorough() + err = c.MaintainThorough(ctx) if err != nil { return } @@ -39,100 +34,21 @@ func MaintainThorough() (err error) { } // MaintainRecordStates runs record state lifecycle maintenance on all storages. -func MaintainRecordStates(ctx context.Context) error { //nolint:gocognit - // TODO: Put this in the storage interface to correctly maintain on all storages. - // Storages might check for deletion and expiry in the query interface and not return anything here. - - // listen for ctx cancel - stop := abool.New() - doneCh := make(chan struct{}) // for goroutine cleanup - defer close(doneCh) - go func() { - select { - case <-ctx.Done(): - case <-doneCh: - } - stop.Set() - }() +func MaintainRecordStates(ctx context.Context) (err error) { + // delete immediately for now + // TODO: increase purge threshold when starting to sync DBs + purgeDeletedBefore := time.Now().UTC() // copy, as we might use the very long all := duplicateControllers() - now := time.Now().Unix() - thirtyDaysAgo := time.Now().Add(-30 * 24 * time.Hour).Unix() - for _, c := range all { - if stop.IsSet() { - return nil - } - - if c.ReadOnly() || c.Injected() { - continue - } - - q, err := query.New("").Check() + err = c.MaintainRecordStates(ctx, purgeDeletedBefore) if err != nil { - return err + return } - - it, err := c.Query(q, true, true) - if err != nil { - return err - } - - var toDelete []record.Record - var toExpire []record.Record - - queryLoop: - for { - select { - case r := <-it.Next: - if r == nil { - break queryLoop - } - - meta := r.Meta() - switch { - case meta.Deleted > 0 && meta.Deleted < thirtyDaysAgo: - toDelete = append(toDelete, r) - case meta.Expires > 0 && meta.Expires < now: - toExpire = append(toExpire, r) - } - case <-ctx.Done(): - it.Cancel() - break queryLoop - } - } - if it.Err() != nil { - return err - } - if stop.IsSet() { - return nil - } - - for _, r := range toDelete { - err := c.storage.Delete(r.DatabaseKey()) - if err != nil { - return err - } - if stop.IsSet() { - return nil - } - } - - for _, r := range toExpire { - r.Meta().Delete() - err := c.Put(r) - if err != nil { - return err - } - if stop.IsSet() { - return nil - } - } - } - return nil + return } func duplicateControllers() (all []*Controller) { diff --git a/database/storage/badger/badger.go b/database/storage/badger/badger.go index 82283d4..7df3155 100644 --- a/database/storage/badger/badger.go +++ b/database/storage/badger/badger.go @@ -1,6 +1,7 @@ package badger import ( + "context" "errors" "fmt" "time" @@ -193,19 +194,25 @@ func (b *Badger) Injected() bool { } // Maintain runs a light maintenance operation on the database. -func (b *Badger) Maintain() error { +func (b *Badger) Maintain(_ context.Context) error { _ = b.db.RunValueLogGC(0.7) return nil } // MaintainThorough runs a thorough maintenance operation on the database. -func (b *Badger) MaintainThorough() (err error) { +func (b *Badger) MaintainThorough(_ context.Context) (err error) { for err == nil { err = b.db.RunValueLogGC(0.7) } return nil } +// MaintainRecordStates maintains records states in the database. +func (b *Badger) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { + // TODO: implement MaintainRecordStates + return nil +} + // Shutdown shuts down the database. func (b *Badger) Shutdown() error { return b.db.Close() diff --git a/database/storage/badger/badger_test.go b/database/storage/badger/badger_test.go index 8cfae5a..6ddb7db 100644 --- a/database/storage/badger/badger_test.go +++ b/database/storage/badger/badger_test.go @@ -2,6 +2,7 @@ package badger import ( + "context" "io/ioutil" "os" "reflect" @@ -116,11 +117,11 @@ func TestBadger(t *testing.T) { } // maintenance - err = db.Maintain() + err = db.Maintain(context.TODO()) if err != nil { t.Fatal(err) } - err = db.MaintainThorough() + err = db.MaintainThorough(context.TODO()) if err != nil { t.Fatal(err) } diff --git a/database/storage/bbolt/bbolt.go b/database/storage/bbolt/bbolt.go index 26666d9..a4d3079 100644 --- a/database/storage/bbolt/bbolt.go +++ b/database/storage/bbolt/bbolt.go @@ -2,6 +2,7 @@ package bbolt import ( "bytes" + "context" "errors" "fmt" "path/filepath" @@ -235,15 +236,69 @@ func (b *BBolt) Injected() bool { } // Maintain runs a light maintenance operation on the database. -func (b *BBolt) Maintain() error { +func (b *BBolt) Maintain(_ context.Context) error { return nil } // MaintainThorough runs a thorough maintenance operation on the database. -func (b *BBolt) MaintainThorough() (err error) { +func (b *BBolt) MaintainThorough(_ context.Context) error { return nil } +// MaintainRecordStates maintains records states in the database. +func (b *BBolt) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { + now := time.Now().Unix() + purgeThreshold := purgeDeletedBefore.Unix() + + return b.db.Update(func(tx *bbolt.Tx) error { + bucket := tx.Bucket(bucketName) + // Create a cursor for iteration. + c := bucket.Cursor() + for key, value := c.First(); key != nil; key, value = c.Next() { + // wrap value + wrapper, err := record.NewRawWrapper(b.name, string(key), value) + if err != nil { + return err + } + + // check if we need to do maintenance + meta := wrapper.Meta() + switch { + case meta.Deleted > 0 && meta.Deleted < purgeThreshold: + // delete from storage + err = c.Delete() + if err != nil { + return err + } + case meta.Expires > 0 && meta.Expires < now: + // mark as deleted + meta.Deleted = meta.Expires + deleted, err := wrapper.MarshalRecord(wrapper) + if err != nil { + return err + } + err = bucket.Put(key, deleted) + if err != nil { + return err + } + + // reposition cursor + c.Seek(key) + } + + time.Sleep(100 * time.Millisecond) + + // check if context is cancelled + select { + case <-ctx.Done(): + return nil + default: + } + } + return nil + }) +} + // Shutdown shuts down the database. func (b *BBolt) Shutdown() error { return b.db.Close() diff --git a/database/storage/bbolt/bbolt_test.go b/database/storage/bbolt/bbolt_test.go index 84586a3..456cd39 100644 --- a/database/storage/bbolt/bbolt_test.go +++ b/database/storage/bbolt/bbolt_test.go @@ -2,11 +2,13 @@ package bbolt import ( + "context" "io/ioutil" "os" "reflect" "sync" "testing" + "time" "github.com/safing/portbase/database/query" "github.com/safing/portbase/database/record" @@ -144,11 +146,15 @@ func TestBBolt(t *testing.T) { } // maintenance - err = db.Maintain() + err = db.Maintain(context.TODO()) if err != nil { t.Fatal(err) } - err = db.MaintainThorough() + err = db.MaintainThorough(context.TODO()) + if err != nil { + t.Fatal(err) + } + err = db.MaintainRecordStates(context.TODO(), time.Now()) if err != nil { t.Fatal(err) } diff --git a/database/storage/fstree/fstree.go b/database/storage/fstree/fstree.go index 34fc9f3..229eed8 100644 --- a/database/storage/fstree/fstree.go +++ b/database/storage/fstree/fstree.go @@ -5,6 +5,7 @@ It is primarily meant for easy testing or storing big files that can easily be a package fstree import ( + "context" "errors" "fmt" "io/ioutil" @@ -255,12 +256,18 @@ func (fst *FSTree) Injected() bool { } // Maintain runs a light maintenance operation on the database. -func (fst *FSTree) Maintain() error { +func (fst *FSTree) Maintain(_ context.Context) error { return nil } // MaintainThorough runs a thorough maintenance operation on the database. -func (fst *FSTree) MaintainThorough() error { +func (fst *FSTree) MaintainThorough(_ context.Context) error { + return nil +} + +// MaintainRecordStates maintains records states in the database. +func (fst *FSTree) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { + // TODO: implement MaintainRecordStates return nil } diff --git a/database/storage/hashmap/map.go b/database/storage/hashmap/map.go index ba6afd6..fb1e073 100644 --- a/database/storage/hashmap/map.go +++ b/database/storage/hashmap/map.go @@ -1,6 +1,7 @@ package hashmap import ( + "context" "errors" "fmt" "sync" @@ -146,12 +147,44 @@ func (hm *HashMap) Injected() bool { } // Maintain runs a light maintenance operation on the database. -func (hm *HashMap) Maintain() error { +func (hm *HashMap) Maintain(_ context.Context) error { return nil } // MaintainThorough runs a thorough maintenance operation on the database. -func (hm *HashMap) MaintainThorough() (err error) { +func (hm *HashMap) MaintainThorough(_ context.Context) error { + return nil +} + +// MaintainRecordStates maintains records states in the database. +func (hm *HashMap) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { + hm.dbLock.Lock() + defer hm.dbLock.Unlock() + + now := time.Now().Unix() + purgeThreshold := purgeDeletedBefore.Unix() + + for key, record := range hm.db { + meta := record.Meta() + switch { + case meta.Deleted > 0 && meta.Deleted < purgeThreshold: + // delete from storage + delete(hm.db, key) + case meta.Expires > 0 && meta.Expires < now: + // mark as deleted + record.Lock() + meta.Deleted = meta.Expires + record.Unlock() + } + + // check if context is cancelled + select { + case <-ctx.Done(): + return nil + default: + } + } + return nil } diff --git a/database/storage/hashmap/map_test.go b/database/storage/hashmap/map_test.go index a546809..36f279b 100644 --- a/database/storage/hashmap/map_test.go +++ b/database/storage/hashmap/map_test.go @@ -2,6 +2,7 @@ package hashmap import ( + "context" "reflect" "sync" "testing" @@ -130,11 +131,11 @@ func TestHashMap(t *testing.T) { } // maintenance - err = db.Maintain() + err = db.Maintain(context.TODO()) if err != nil { t.Fatal(err) } - err = db.MaintainThorough() + err = db.MaintainThorough(context.TODO()) if err != nil { t.Fatal(err) } diff --git a/database/storage/injectbase.go b/database/storage/injectbase.go index 182291c..8dda71d 100644 --- a/database/storage/injectbase.go +++ b/database/storage/injectbase.go @@ -1,7 +1,9 @@ package storage import ( + "context" "errors" + "time" "github.com/safing/portbase/database/iterator" "github.com/safing/portbase/database/query" @@ -54,12 +56,17 @@ func (i *InjectBase) Injected() bool { } // Maintain runs a light maintenance operation on the database. -func (i *InjectBase) Maintain() error { +func (i *InjectBase) Maintain(ctx context.Context) error { return nil } // MaintainThorough runs a thorough maintenance operation on the database. -func (i *InjectBase) MaintainThorough() error { +func (i *InjectBase) MaintainThorough(ctx context.Context) error { + return nil +} + +// MaintainRecordStates maintains records states in the database. +func (i *InjectBase) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { return nil } diff --git a/database/storage/interface.go b/database/storage/interface.go index bdf12e4..4ec5232 100644 --- a/database/storage/interface.go +++ b/database/storage/interface.go @@ -1,6 +1,9 @@ package storage import ( + "context" + "time" + "github.com/safing/portbase/database/iterator" "github.com/safing/portbase/database/query" "github.com/safing/portbase/database/record" @@ -15,8 +18,9 @@ type Interface interface { ReadOnly() bool Injected() bool - Maintain() error - MaintainThorough() error + Maintain(ctx context.Context) error + MaintainThorough(ctx context.Context) error + MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error Shutdown() error } diff --git a/database/storage/sinkhole/sinkhole.go b/database/storage/sinkhole/sinkhole.go index dd3b065..b7e4627 100644 --- a/database/storage/sinkhole/sinkhole.go +++ b/database/storage/sinkhole/sinkhole.go @@ -1,7 +1,9 @@ package sinkhole import ( + "context" "errors" + "time" "github.com/safing/portbase/database/iterator" "github.com/safing/portbase/database/query" @@ -77,12 +79,17 @@ func (s *Sinkhole) Injected() bool { } // Maintain runs a light maintenance operation on the database. -func (s *Sinkhole) Maintain() error { +func (s *Sinkhole) Maintain(ctx context.Context) error { return nil } // MaintainThorough runs a thorough maintenance operation on the database. -func (s *Sinkhole) MaintainThorough() (err error) { +func (s *Sinkhole) MaintainThorough(ctx context.Context) error { + return nil +} + +// MaintainRecordStates maintains records states in the database. +func (s *Sinkhole) MaintainRecordStates(ctx context.Context, purgeDeletedBefore time.Time) error { return nil }