diff --git a/basic_ds.go b/basic_ds.go index f63d8af..0581cef 100644 --- a/basic_ds.go +++ b/basic_ds.go @@ -226,3 +226,24 @@ func (d *LogDatastore) Close() error { } return nil } + +func (d *LogDatastore) Check() error { + if c, ok := d.child.(CheckedDatastore); ok { + return c.Check() + } + return nil +} + +func (d *LogDatastore) Scrub() error { + if c, ok := d.child.(ScrubbedDatastore); ok { + return c.Scrub() + } + return nil +} + +func (d *LogDatastore) CollectGarbage() error { + if c, ok := d.child.(GCDatastore); ok { + return c.CollectGarbage() + } + return nil +} diff --git a/keytransform/keytransform.go b/keytransform/keytransform.go index fc83636..9cb2805 100644 --- a/keytransform/keytransform.go +++ b/keytransform/keytransform.go @@ -117,3 +117,24 @@ func (t *transformBatch) Delete(key ds.Key) error { func (t *transformBatch) Commit() error { return t.dst.Commit() } + +func (d *ktds) Check() error { + if c, ok := d.child.(ds.CheckedDatastore); ok { + return c.Check() + } + return nil +} + +func (d *ktds) Scrub() error { + if c, ok := d.child.(ds.ScrubbedDatastore); ok { + return c.Scrub() + } + return nil +} + +func (d *ktds) CollectGarbage() error { + if c, ok := d.child.(ds.GCDatastore); ok { + return c.CollectGarbage() + } + return nil +} diff --git a/keytransform/keytransform_test.go b/keytransform/keytransform_test.go index 1c50f3f..b0cc562 100644 --- a/keytransform/keytransform_test.go +++ b/keytransform/keytransform_test.go @@ -10,6 +10,7 @@ import ( ds "github.com/ipfs/go-datastore" kt "github.com/ipfs/go-datastore/keytransform" dsq "github.com/ipfs/go-datastore/query" + dstest "github.com/ipfs/go-datastore/test" ) // Hook up gocheck into the "go test" runner. @@ -35,7 +36,7 @@ func (ks *DSSuite) TestBasic(c *C) { }, } - mpds := ds.NewMapDatastore() + mpds := dstest.NewTestDatastore(true) ktds := kt.Wrap(mpds, pair) keys := strsToKeys([]string{ @@ -88,6 +89,18 @@ func (ks *DSSuite) TestBasic(c *C) { c.Log("listA: ", listA) c.Log("listB: ", listB) + + if err := ktds.Check(); err != dstest.TestError { + c.Errorf("Unexpected Check() error: %s", err) + } + + if err := ktds.CollectGarbage(); err != dstest.TestError { + c.Errorf("Unexpected CollectGarbage() error: %s", err) + } + + if err := ktds.Scrub(); err != dstest.TestError { + c.Errorf("Unexpected Scrub() error: %s", err) + } } func strsToKeys(strs []string) []ds.Key { diff --git a/mount/mount.go b/mount/mount.go index d386829..82c2e9f 100644 --- a/mount/mount.go +++ b/mount/mount.go @@ -4,6 +4,7 @@ package mount import ( "errors" + "fmt" "io" "sort" "strings" @@ -251,3 +252,36 @@ func (mt *mountBatch) Commit() error { } return nil } + +func (d *Datastore) Check() error { + for _, m := range d.mounts { + if c, ok := m.Datastore.(datastore.CheckedDatastore); ok { + if err := c.Check(); err != nil { + return fmt.Errorf("checking datastore at %s: %s", m.Prefix.String(), err.Error()) + } + } + } + return nil +} + +func (d *Datastore) Scrub() error { + for _, m := range d.mounts { + if c, ok := m.Datastore.(datastore.ScrubbedDatastore); ok { + if err := c.Scrub(); err != nil { + return fmt.Errorf("scrubbing datastore at %s: %s", m.Prefix.String(), err.Error()) + } + } + } + return nil +} + +func (d *Datastore) CollectGarbage() error { + for _, m := range d.mounts { + if c, ok := m.Datastore.(datastore.GCDatastore); ok { + if err := c.CollectGarbage(); err != nil { + return fmt.Errorf("gc on datastore at %s: %s", m.Prefix.String(), err.Error()) + } + } + } + return nil +} diff --git a/mount/mount_test.go b/mount/mount_test.go index c6cd242..35f126b 100644 --- a/mount/mount_test.go +++ b/mount/mount_test.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/mount" "github.com/ipfs/go-datastore/query" + dstest "github.com/ipfs/go-datastore/test" ) func TestPutBadNothing(t *testing.T) { @@ -371,3 +372,22 @@ func TestErrQueryClose(t *testing.T) { t.Errorf("Query was ok or q.Error was nil") } } + +func TestMaintenanceFunctions(t *testing.T) { + mapds := dstest.NewTestDatastore(true) + m := mount.New([]mount.Mount{ + {Prefix: datastore.NewKey("/"), Datastore: mapds}, + }) + + if err:= m.Check(); err.Error() != "checking datastore at /: test error" { + t.Errorf("Unexpected Check() error: %s", err) + } + + if err:= m.CollectGarbage(); err.Error() != "gc on datastore at /: test error" { + t.Errorf("Unexpected CollectGarbage() error: %s", err) + } + + if err:= m.Scrub(); err.Error() != "scrubbing datastore at /: test error" { + t.Errorf("Unexpected Scrub() error: %s", err) + } +} diff --git a/namespace/namespace_test.go b/namespace/namespace_test.go index 9833f97..716de1b 100644 --- a/namespace/namespace_test.go +++ b/namespace/namespace_test.go @@ -10,6 +10,7 @@ import ( ds "github.com/ipfs/go-datastore" ns "github.com/ipfs/go-datastore/namespace" dsq "github.com/ipfs/go-datastore/query" + dstest "github.com/ipfs/go-datastore/test" ) // Hook up gocheck into the "go test" runner. @@ -79,7 +80,7 @@ func (ks *DSSuite) testBasic(c *C, prefix string) { } func (ks *DSSuite) TestQuery(c *C) { - mpds := ds.NewMapDatastore() + mpds := dstest.NewTestDatastore(true) nsds := ns.Wrap(mpds, ds.NewKey("/foo")) keys := strsToKeys([]string{ @@ -137,6 +138,18 @@ func (ks *DSSuite) TestQuery(c *C) { expval, _ := expect[i].Value.([]byte) c.Check(string(entval), Equals, string(expval)) } + + if err := nsds.Datastore.(ds.CheckedDatastore).Check(); err != dstest.TestError { + c.Errorf("Unexpected Check() error: %s", err) + } + + if err := nsds.Datastore.(ds.GCDatastore).CollectGarbage(); err != dstest.TestError { + c.Errorf("Unexpected CollectGarbage() error: %s", err) + } + + if err := nsds.Datastore.(ds.ScrubbedDatastore).Scrub(); err != dstest.TestError { + c.Errorf("Unexpected Scrub() error: %s", err) + } } func strsToKeys(strs []string) []ds.Key { diff --git a/sync/sync.go b/sync/sync.go index 9b94979..9c74f1c 100644 --- a/sync/sync.go +++ b/sync/sync.go @@ -114,3 +114,24 @@ func (b *syncBatch) Commit() error { defer b.mds.Unlock() return b.batch.Commit() } + +func (d *MutexDatastore) Check() error { + if c, ok := d.child.(ds.CheckedDatastore); ok { + return c.Check() + } + return nil +} + +func (d *MutexDatastore) Scrub() error { + if c, ok := d.child.(ds.ScrubbedDatastore); ok { + return c.Scrub() + } + return nil +} + +func (d *MutexDatastore) CollectGarbage() error { + if c, ok := d.child.(ds.GCDatastore); ok { + return c.CollectGarbage() + } + return nil +} diff --git a/syncmount/mount.go b/syncmount/mount.go index c10029a..64ee462 100644 --- a/syncmount/mount.go +++ b/syncmount/mount.go @@ -4,6 +4,7 @@ package syncmount import ( "errors" + "fmt" "io" "sort" "strings" @@ -264,3 +265,36 @@ func (mt *mountBatch) Commit() error { } return nil } + +func (d *Datastore) Check() error { + for _, m := range d.mounts { + if c, ok := m.Datastore.(ds.CheckedDatastore); ok { + if err := c.Check(); err != nil { + return fmt.Errorf("checking datastore at %s: %s", m.Prefix.String(), err.Error()) + } + } + } + return nil +} + +func (d *Datastore) Scrub() error { + for _, m := range d.mounts { + if c, ok := m.Datastore.(ds.ScrubbedDatastore); ok { + if err := c.Scrub(); err != nil { + return fmt.Errorf("scrubbing datastore at %s: %s", m.Prefix.String(), err.Error()) + } + } + } + return nil +} + +func (d *Datastore) CollectGarbage() error { + for _, m := range d.mounts { + if c, ok := m.Datastore.(ds.GCDatastore); ok { + if err := c.CollectGarbage(); err != nil { + return fmt.Errorf("gc on datastore at %s: %s", m.Prefix.String(), err.Error()) + } + } + } + return nil +} diff --git a/syncmount/mount_test.go b/syncmount/mount_test.go index 6abe889..b140fc3 100644 --- a/syncmount/mount_test.go +++ b/syncmount/mount_test.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" mount "github.com/ipfs/go-datastore/syncmount" + dstest "github.com/ipfs/go-datastore/test" ) func TestPutBadNothing(t *testing.T) { @@ -371,3 +372,22 @@ func TestErrQueryClose(t *testing.T) { t.Errorf("Query was ok or q.Error was nil") } } + +func TestMaintenanceFunctions(t *testing.T) { + mapds := dstest.NewTestDatastore(true) + m := mount.New([]mount.Mount{ + {Prefix: datastore.NewKey("/"), Datastore: mapds}, + }) + + if err:= m.Check(); err.Error() != "checking datastore at /: test error" { + t.Errorf("Unexpected Check() error: %s", err) + } + + if err:= m.CollectGarbage(); err.Error() != "gc on datastore at /: test error" { + t.Errorf("Unexpected CollectGarbage() error: %s", err) + } + + if err:= m.Scrub(); err.Error() != "scrubbing datastore at /: test error" { + t.Errorf("Unexpected Scrub() error: %s", err) + } +} diff --git a/test/test_util.go b/test/test_util.go index 38ea5d0..68f8c7b 100644 --- a/test/test_util.go +++ b/test/test_util.go @@ -3,12 +3,17 @@ package dstest import ( "bytes" "encoding/base32" + "errors" "math/rand" "testing" dstore "github.com/ipfs/go-datastore" ) +var ( + TestError = errors.New("test error") +) + func RunBatchTest(t *testing.T, ds dstore.Batching) { batch, err := ds.Batch() if err != nil { @@ -95,3 +100,37 @@ func RunBatchDeleteTest(t *testing.T, ds dstore.Batching) { } } } + +type testDatastore struct { + testErrors bool + + *dstore.MapDatastore +} + +func NewTestDatastore(testErrors bool) *testDatastore { + return &testDatastore{ + testErrors: testErrors, + MapDatastore: dstore.NewMapDatastore(), + } +} + +func (d *testDatastore) Check() error { + if d.testErrors { + return TestError + } + return nil +} + +func (d *testDatastore) Scrub() error { + if d.testErrors { + return TestError + } + return nil +} + +func (d *testDatastore) CollectGarbage() error { + if d.testErrors { + return TestError + } + return nil +} diff --git a/tiered/tiered.go b/tiered/tiered.go index 06f0535..e4b6d5a 100644 --- a/tiered/tiered.go +++ b/tiered/tiered.go @@ -92,3 +92,36 @@ func (d tiered) Query(q dsq.Query) (dsq.Results, error) { // query always the last (most complete) one return d[len(d)-1].Query(q) } + +func (d tiered) Check() error { + for i, child := range d { + if c, ok := child.(ds.CheckedDatastore); ok { + if err := c.Check(); err != nil { + return fmt.Errorf("checking datastore %d: %s", i, err.Error()) + } + } + } + return nil +} + +func (d tiered) Scrub() error { + for i, child := range d { + if c, ok := child.(ds.ScrubbedDatastore); ok { + if err := c.Scrub(); err != nil { + return fmt.Errorf("scrubbing datastore at %d: %s", i, err.Error()) + } + } + } + return nil +} + +func (d tiered) CollectGarbage() error { + for i, child := range d { + if c, ok := child.(ds.GCDatastore); ok { + if err := c.CollectGarbage(); err != nil { + return fmt.Errorf("gc on datastore at %d: %s", i, err.Error()) + } + } + } + return nil +}