diff --git a/.gx/lastpubver b/.gx/lastpubver index 9638fb7..2dd3e53 100644 --- a/.gx/lastpubver +++ b/.gx/lastpubver @@ -1 +1 @@ -3.3.0: QmbQshXLNcCPRUGZv4sBGxnZNAHREA6MKeomkwihNXPZWP +3.4.0: QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D diff --git a/autobatch/autobatch.go b/autobatch/autobatch.go index cb9c5fa..64d68bd 100644 --- a/autobatch/autobatch.go +++ b/autobatch/autobatch.go @@ -88,6 +88,16 @@ func (d *Datastore) Has(k ds.Key) (bool, error) { return d.child.Has(k) } +// GetSize implements Datastore.GetSize +func (d *Datastore) GetSize(k ds.Key) (int, error) { + v, ok := d.buffer[k] + if ok { + return len(v), nil + } + + return d.child.GetSize(k) +} + // Query performs a query func (d *Datastore) Query(q dsq.Query) (dsq.Results, error) { err := d.Flush() diff --git a/basic_ds.go b/basic_ds.go index dbcb999..2cb7568 100644 --- a/basic_ds.go +++ b/basic_ds.go @@ -42,6 +42,14 @@ func (d *MapDatastore) Has(key Key) (exists bool, err error) { return found, nil } +// GetSize implements Datastore.GetSize +func (d *MapDatastore) GetSize(key Key) (size int, err error) { + if v, found := d.values[key]; found { + return len(v), nil + } + return -1, ErrNotFound +} + // Delete implements Datastore.Delete func (d *MapDatastore) Delete(key Key) (err error) { if _, found := d.values[key]; !found { @@ -95,6 +103,11 @@ func (d *NullDatastore) Has(key Key) (exists bool, err error) { return false, nil } +// Has implements Datastore.GetSize +func (d *NullDatastore) GetSize(key Key) (size int, err error) { + return -1, ErrNotFound +} + // Delete implements Datastore.Delete func (d *NullDatastore) Delete(key Key) (err error) { return nil @@ -158,6 +171,12 @@ func (d *LogDatastore) Has(key Key) (exists bool, err error) { return d.child.Has(key) } +// GetSize implements Datastore.GetSize +func (d *LogDatastore) GetSize(key Key) (size int, err error) { + log.Printf("%s: GetSize %s\n", d.Name, key) + return d.child.GetSize(key) +} + // Delete implements Datastore.Delete func (d *LogDatastore) Delete(key Key) (err error) { log.Printf("%s: Delete %s\n", d.Name, key) diff --git a/datastore.go b/datastore.go index 63263ed..c6558ef 100644 --- a/datastore.go +++ b/datastore.go @@ -52,6 +52,11 @@ type Datastore interface { // The default implementation is found in `GetBackedHas`. Has(key Key) (exists bool, err error) + // GetSize returns the size of the `value` named by `key`. + // In some contexts, it may be much cheaper to only get the size of the + // value rather than retrieving the value itself. + GetSize(key Key) (size int, err error) + // Delete removes the value for given `key`. Delete(key Key) error @@ -197,6 +202,20 @@ func GetBackedHas(ds Datastore, key Key) (bool, error) { } } +// GetBackedSize provides a default Datastore.GetSize implementation. +// It exists so Datastore.GetSize implementations can use it, like so: +// +// func (*d SomeDatastore) GetSize(key Key) (size int, err error) { +// return GetBackedSize(d, key) +// } +func GetBackedSize(ds Datastore, key Key) (int, error) { + value, err := ds.Get(key) + if err == nil { + return len(value), nil + } + return -1, err +} + type Batch interface { Put(key Key, val []byte) error diff --git a/delayed/delayed.go b/delayed/delayed.go index da02a11..a8bfb18 100644 --- a/delayed/delayed.go +++ b/delayed/delayed.go @@ -33,6 +33,11 @@ func (dds *delayed) Has(key ds.Key) (exists bool, err error) { return dds.ds.Has(key) } +func (dds *delayed) GetSize(key ds.Key) (size int, err error) { + dds.delay.Wait() + return dds.ds.GetSize(key) +} + func (dds *delayed) Delete(key ds.Key) (err error) { dds.delay.Wait() return dds.ds.Delete(key) diff --git a/examples/fs.go b/examples/fs.go index ea59d7e..94cefab 100644 --- a/examples/fs.go +++ b/examples/fs.go @@ -78,6 +78,10 @@ func (d *Datastore) Has(key ds.Key) (exists bool, err error) { return ds.GetBackedHas(d, key) } +func (d *Datastore) GetSize(key ds.Key) (size int, err error) { + return ds.GetBackedSize(d, key) +} + // Delete removes the value for given key func (d *Datastore) Delete(key ds.Key) (err error) { fn := d.KeyFilename(key) diff --git a/failstore/failstore.go b/failstore/failstore.go index 222c3be..feee789 100644 --- a/failstore/failstore.go +++ b/failstore/failstore.go @@ -56,6 +56,16 @@ func (d *Failstore) Has(k ds.Key) (bool, error) { return d.child.Has(k) } +// GetSize returns the size of the value in the datastore, if present. +func (d *Failstore) GetSize(k ds.Key) (int, error) { + err := d.errfunc("getsize") + if err != nil { + return -1, err + } + + return d.child.GetSize(k) +} + // Delete removes a key/value from the datastore. func (d *Failstore) Delete(k ds.Key) error { err := d.errfunc("delete") diff --git a/keytransform/keytransform.go b/keytransform/keytransform.go index 8fb7c41..be7db57 100644 --- a/keytransform/keytransform.go +++ b/keytransform/keytransform.go @@ -48,6 +48,12 @@ func (d *ktds) Has(key ds.Key) (exists bool, err error) { return d.child.Has(d.ConvertKey(key)) } +// GetSize returns the size of the value named by the given key, transforming +// the key first. +func (d *ktds) GetSize(key ds.Key) (size int, err error) { + return d.child.GetSize(d.ConvertKey(key)) +} + // Delete removes the value for given key func (d *ktds) Delete(key ds.Key) (err error) { return d.child.Delete(d.ConvertKey(key)) diff --git a/mount/mount.go b/mount/mount.go index d6aa920..8f2a89e 100644 --- a/mount/mount.go +++ b/mount/mount.go @@ -106,6 +106,14 @@ func (d *Datastore) Has(key ds.Key) (exists bool, err error) { return cds.Has(k) } +func (d *Datastore) GetSize(key ds.Key) (size int, err error) { + cds, _, k := d.lookup(key) + if cds == nil { + return -1, ds.ErrNotFound + } + return cds.GetSize(k) +} + func (d *Datastore) Delete(key ds.Key) error { cds, _, k := d.lookup(key) if cds == nil { diff --git a/package.json b/package.json index 64f2ea2..d0b8951 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,6 @@ "license": "MIT", "name": "go-datastore", "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", - "version": "3.3.0" + "version": "3.4.0" } diff --git a/retrystore/retrystore.go b/retrystore/retrystore.go index 7b3cd0e..a03daa8 100644 --- a/retrystore/retrystore.go +++ b/retrystore/retrystore.go @@ -83,3 +83,14 @@ func (d *Datastore) Has(k ds.Key) (bool, error) { }) return has, err } + +// GetSize returns the size of the value in the datastore, if present. +func (d *Datastore) GetSize(k ds.Key) (int, error) { + var size int + err := d.runOp(func() error { + var err error + size, err = d.Batching.GetSize(k) + return err + }) + return size, err +} diff --git a/sync/sync.go b/sync/sync.go index 36fd0f7..2218483 100644 --- a/sync/sync.go +++ b/sync/sync.go @@ -51,6 +51,13 @@ func (d *MutexDatastore) Has(key ds.Key) (exists bool, err error) { return d.child.Has(key) } +// GetSize implements Datastore.GetSize +func (d *MutexDatastore) GetSize(key ds.Key) (size int, err error) { + d.RLock() + defer d.RUnlock() + return d.child.GetSize(key) +} + // Delete implements Datastore.Delete func (d *MutexDatastore) Delete(key ds.Key) (err error) { d.Lock() diff --git a/test/basic_tests.go b/test/basic_tests.go index 4059756..4f87a52 100644 --- a/test/basic_tests.go +++ b/test/basic_tests.go @@ -29,6 +29,14 @@ func SubtestBasicPutGet(t *testing.T, ds dstore.Datastore) { t.Fatal("should have key foo, has returned false") } + size, err := ds.GetSize(k) + if err != nil { + t.Fatal("error getting size after put: ", err) + } + if size != len(val) { + t.Fatalf("incorrect size: expected %d, got %d", len(val), size) + } + out, err := ds.Get(k) if err != nil { t.Fatal("error getting value after put: ", err) @@ -47,6 +55,14 @@ func SubtestBasicPutGet(t *testing.T, ds dstore.Datastore) { t.Fatal("should have key foo, has returned false") } + size, err = ds.GetSize(k) + if err != nil { + t.Fatal("error getting size after get: ", err) + } + if size != len(val) { + t.Fatalf("incorrect size: expected %d, got %d", len(val), size) + } + err = ds.Delete(k) if err != nil { t.Fatal("error calling delete: ", err) @@ -60,6 +76,18 @@ func SubtestBasicPutGet(t *testing.T, ds dstore.Datastore) { if have { t.Fatal("should not have key foo, has returned true") } + + size, err = ds.GetSize(k) + switch err { + case dstore.ErrNotFound: + case nil: + t.Fatal("expected error getting size after delete") + default: + t.Fatal("wrong error getting size after delete: ", err) + } + if size != -1 { + t.Fatal("expected missing size to be -1") + } } func SubtestNotFounds(t *testing.T, ds dstore.Datastore) { @@ -81,6 +109,18 @@ func SubtestNotFounds(t *testing.T, ds dstore.Datastore) { if have { t.Fatal("has returned true for key we don't have") } + + size, err := ds.GetSize(badk) + switch err { + case dstore.ErrNotFound: + case nil: + t.Fatal("expected error getting size after delete") + default: + t.Fatal("wrong error getting size after delete: ", err) + } + if size != -1 { + t.Fatal("expected missing size to be -1") + } } func SubtestManyKeysAndQuery(t *testing.T, ds dstore.Datastore) {