syncbase: various small additions and enhancements
- stop returning error from Table.Scan
- make app/db/table delete idempotent
- implement Table.DeleteRowRange
- add basic perms check to Table.Scan
- various comment and TODO tweaks
- drop WrapError, use verror.Convert
Change-Id: Ib3d34c14751d39c2c7e324945ab30e347cd07e30
diff --git a/services/syncbase/server/app.go b/services/syncbase/server/app.go
index 6e1812f..af92c09 100644
--- a/services/syncbase/server/app.go
+++ b/services/syncbase/server/app.go
@@ -87,7 +87,7 @@
defer a.mu.Unlock()
d, ok := a.dbs[dbName]
if !ok {
- return nil, verror.New(verror.ErrNoExistOrNoAccess, ctx, dbName)
+ return nil, verror.New(verror.ErrNoExist, ctx, dbName)
}
return d, nil
}
@@ -128,7 +128,7 @@
return err
}
// Check for "database already exists".
- if _, err := a.getDbInfo(ctx, call, st, dbName); verror.ErrorID(err) != verror.ErrNoExistOrNoAccess.ID {
+ if _, err := a.getDbInfo(ctx, call, st, dbName); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return err
}
@@ -181,11 +181,14 @@
defer a.mu.Unlock()
d, ok := a.dbs[dbName]
if !ok {
- return verror.New(verror.ErrNoExistOrNoAccess, ctx, dbName)
+ return nil // delete is idempotent
}
// 1. Check databaseData perms.
if err := d.CheckPermsInternal(ctx, call, d.St()); err != nil {
+ if verror.ErrorID(err) == verror.ErrNoExist.ID {
+ return nil // delete is idempotent
+ }
return err
}
@@ -216,7 +219,7 @@
defer a.mu.Unlock()
d, ok := a.dbs[dbName]
if !ok {
- return verror.New(verror.ErrNoExistOrNoAccess, ctx, dbName)
+ return verror.New(verror.ErrNoExist, ctx, dbName)
}
return d.SetPermsInternal(ctx, call, perms, version)
}
diff --git a/services/syncbase/server/dispatcher.go b/services/syncbase/server/dispatcher.go
index 7f2fd1f..f6a4dde 100644
--- a/services/syncbase/server/dispatcher.go
+++ b/services/syncbase/server/dispatcher.go
@@ -52,7 +52,7 @@
a = aint.(*app) // panics on failure, as desired
aExists = true
} else {
- if verror.ErrorID(err) != verror.ErrNoExistOrNoAccess.ID {
+ if verror.ErrorID(err) != verror.ErrNoExist.ID {
return nil, nil, err
} else {
a = &app{
@@ -69,7 +69,7 @@
// All database, table, and row methods require the app to exist. If it
// doesn't, abort early.
if !aExists {
- return nil, nil, verror.New(verror.ErrNoExistOrNoAccess, nil, a.name)
+ return nil, nil, verror.New(verror.ErrNoExist, nil, a.name)
}
// Note, it's possible for the app to be deleted concurrently with downstream
diff --git a/services/syncbase/server/nosql/dispatcher.go b/services/syncbase/server/nosql/dispatcher.go
index 6d572ae..d8fe2f0 100644
--- a/services/syncbase/server/nosql/dispatcher.go
+++ b/services/syncbase/server/nosql/dispatcher.go
@@ -49,7 +49,7 @@
d = dint.(*database) // panics on failure, as desired
dExists = true
} else {
- if verror.ErrorID(err) != verror.ErrNoExistOrNoAccess.ID {
+ if verror.ErrorID(err) != verror.ErrNoExist.ID {
return nil, nil, err
} else {
d = &database{
@@ -66,7 +66,7 @@
// All table and row methods require the database to exist. If it doesn't,
// abort early.
if !dExists {
- return nil, nil, verror.New(verror.ErrNoExistOrNoAccess, nil, d.name)
+ return nil, nil, verror.New(verror.ErrNoExist, nil, d.name)
}
// Note, it's possible for the database to be deleted concurrently with
diff --git a/services/syncbase/server/nosql/row.go b/services/syncbase/server/nosql/row.go
index f91af3b..057dc10 100644
--- a/services/syncbase/server/nosql/row.go
+++ b/services/syncbase/server/nosql/row.go
@@ -80,8 +80,6 @@
value, err := st.Get([]byte(r.StKey()), nil)
if err != nil {
if verror.ErrorID(err) == store.ErrUnknownKey.ID {
- // We've already done an auth check, so here we can safely return NoExist
- // rather than NoExistOrNoAccess.
return nil, verror.New(verror.ErrNoExist, ctx, r.Name())
}
return nil, verror.New(verror.ErrInternal, ctx, err)
diff --git a/services/syncbase/server/nosql/table.go b/services/syncbase/server/nosql/table.go
index eb2a9c5..e73afd2 100644
--- a/services/syncbase/server/nosql/table.go
+++ b/services/syncbase/server/nosql/table.go
@@ -37,7 +37,7 @@
return err
}
// Check for "table already exists".
- if err := util.GetWithoutAuth(ctx, call, st, t, &tableData{}); verror.ErrorID(err) != verror.ErrNoExistOrNoAccess.ID {
+ if err := util.GetWithoutAuth(ctx, call, st, t, &tableData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return err
}
@@ -60,6 +60,9 @@
return store.RunInTransaction(t.d.st, func(st store.StoreReadWriter) error {
// Read-check-delete tableData.
if err := util.Get(ctx, call, st, t, &tableData{}); err != nil {
+ if verror.ErrorID(err) == verror.ErrNoExist.ID {
+ return nil // delete is idempotent
+ }
return err
}
// TODO(sadovsky): Delete all rows in this table.
@@ -68,12 +71,33 @@
}
func (t *table) DeleteRowRange(ctx *context.T, call rpc.ServerCall, start, limit []byte) error {
- return verror.NewErrNotImplemented(ctx)
+ return store.RunInTransaction(t.d.st, func(st store.StoreReadWriter) error {
+ // Check perms.
+ if err := util.Get(ctx, call, st, t, &tableData{}); err != nil {
+ return err
+ }
+ it := st.Scan(util.ScanRangeArgs(util.JoinKeyParts(util.RowPrefix, t.name), string(start), string(limit)))
+ key := []byte{}
+ for it.Advance() {
+ key = it.Key(key)
+ if err := st.Delete(key); err != nil {
+ return verror.New(verror.ErrInternal, ctx, err)
+ }
+ }
+ if err := it.Err(); err != nil {
+ return verror.New(verror.ErrInternal, ctx, err)
+ }
+ return nil
+ })
}
func (t *table) Scan(ctx *context.T, call wire.TableScanServerCall, start, limit []byte) error {
sn := t.d.st.NewSnapshot()
defer sn.Close()
+ // Check perms.
+ if err := util.Get(ctx, call, sn, t, &tableData{}); err != nil {
+ return err
+ }
it := sn.Scan(util.ScanRangeArgs(util.JoinKeyParts(util.RowPrefix, t.name), string(start), string(limit)))
sender := call.SendStream()
key, value := []byte{}, []byte{}
@@ -117,8 +141,8 @@
}
func (t *table) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
- // Check perms.
sn := t.d.st.NewSnapshot()
+ // Check perms.
if err := util.Get(ctx, call, sn, t, &tableData{}); err != nil {
sn.Close()
return nil, err
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index c5f6be4..b352a27 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -4,6 +4,10 @@
package server
+// TODO(sadovsky): Check Resolve access on parent where applicable. Relatedly,
+// convert ErrNoExist and ErrNoAccess to ErrNoExistOrNoAccess where needed to
+// preserve privacy.
+
import (
"sync"
@@ -113,7 +117,7 @@
defer s.mu.Unlock()
a, ok := s.apps[appName]
if !ok {
- return nil, verror.New(verror.ErrNoExistOrNoAccess, ctx, appName)
+ return nil, verror.New(verror.ErrNoExist, ctx, appName)
}
return a, nil
}
@@ -137,7 +141,6 @@
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.apps[appName]; ok {
- // TODO(sadovsky): Should this be ErrExistOrNoAccess, for privacy?
return verror.New(verror.ErrExist, ctx, appName)
}
@@ -154,11 +157,10 @@
return err
}
// Check for "app already exists".
- if err := util.GetWithoutAuth(ctx, call, st, a, &appData{}); verror.ErrorID(err) != verror.ErrNoExistOrNoAccess.ID {
+ if err := util.GetWithoutAuth(ctx, call, st, a, &appData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return err
}
- // TODO(sadovsky): Should this be ErrExistOrNoAccess, for privacy?
return verror.New(verror.ErrExist, ctx, appName)
}
// Write new appData.
@@ -183,13 +185,15 @@
defer s.mu.Unlock()
a, ok := s.apps[appName]
if !ok {
- // TODO(sadovsky): Make delete idempotent, here and elsewhere.
- return verror.New(verror.ErrNoExistOrNoAccess, ctx, appName)
+ return nil // delete is idempotent
}
if err := store.RunInTransaction(s.st, func(st store.StoreReadWriter) error {
// Read-check-delete appData.
if err := util.Get(ctx, call, st, a, &appData{}); err != nil {
+ if verror.ErrorID(err) == verror.ErrNoExist.ID {
+ return nil // delete is idempotent
+ }
return err
}
// TODO(sadovsky): Delete all databases in this app.
@@ -207,7 +211,7 @@
defer s.mu.Unlock()
a, ok := s.apps[appName]
if !ok {
- return verror.New(verror.ErrNoExistOrNoAccess, ctx, appName)
+ return verror.New(verror.ErrNoExist, ctx, appName)
}
return store.RunInTransaction(s.st, func(st store.StoreReadWriter) error {
data := &appData{}
diff --git a/services/syncbase/server/util/store_util.go b/services/syncbase/server/util/store_util.go
index 69be3c5..8e289d6 100644
--- a/services/syncbase/server/util/store_util.go
+++ b/services/syncbase/server/util/store_util.go
@@ -47,8 +47,7 @@
func GetWithoutAuth(ctx *context.T, call rpc.ServerCall, st store.StoreReader, l Layer, v interface{}) error {
if err := GetObject(st, l.StKey(), v); err != nil {
if verror.ErrorID(err) == store.ErrUnknownKey.ID {
- // TODO(sadovsky): Return ErrNoExist if appropriate.
- return verror.New(verror.ErrNoExistOrNoAccess, ctx, l.Name())
+ return verror.New(verror.ErrNoExist, ctx, l.Name())
}
return verror.New(verror.ErrInternal, ctx, err)
}
@@ -63,8 +62,7 @@
}
auth, _ := access.PermissionsAuthorizer(v.GetPerms(), access.TypicalTagType())
if err := auth.Authorize(ctx, call.Security()); err != nil {
- // TODO(sadovsky): Return ErrNoAccess if appropriate.
- return verror.New(verror.ErrNoExistOrNoAccess, ctx, l.Name())
+ return verror.New(verror.ErrNoAccess, ctx, l.Name())
}
return nil
}
diff --git a/services/syncbase/server/watchable/store.go b/services/syncbase/server/watchable/store.go
index 3e0e7d3..44faa54 100644
--- a/services/syncbase/server/watchable/store.go
+++ b/services/syncbase/server/watchable/store.go
@@ -87,10 +87,6 @@
var _ Store = (*wstore)(nil)
-// TODO(sadovsky): Decide whether to copy []byte's vs. requiring clients not to
-// modify passed-in []byte's. (In fact, this should be spelled out in the
-// store.Store API contract.)
-
// Close implements the store.Store interface.
func (st *wstore) Close() error {
return st.ist.Close()
diff --git a/services/syncbase/server/watchable/stream.go b/services/syncbase/server/watchable/stream.go
index cd41835..9c8c392 100644
--- a/services/syncbase/server/watchable/stream.go
+++ b/services/syncbase/server/watchable/stream.go
@@ -79,7 +79,7 @@
s.mu.Lock()
defer s.mu.Unlock()
if s.err != nil {
- return store.WrapError(s.err)
+ return convertError(s.err)
}
return s.iit.Err()
}
diff --git a/services/syncbase/server/watchable/transaction.go b/services/syncbase/server/watchable/transaction.go
index 1be1b85..ace48de 100644
--- a/services/syncbase/server/watchable/transaction.go
+++ b/services/syncbase/server/watchable/transaction.go
@@ -36,7 +36,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return valbuf, store.WrapError(tx.err)
+ return valbuf, convertError(tx.err)
}
var err error
if !tx.st.managesKey(key) {
@@ -70,7 +70,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
var err error
if !tx.st.managesKey(key) {
@@ -87,7 +87,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
var err error
if !tx.st.managesKey(key) {
@@ -104,7 +104,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
tx.err = verror.New(verror.ErrBadState, nil, store.ErrMsgCommittedTxn)
tx.st.mu.Lock()
@@ -144,7 +144,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
tx.err = verror.New(verror.ErrCanceled, nil, store.ErrMsgAbortedTxn)
return tx.itx.Abort()
diff --git a/services/syncbase/server/watchable/util.go b/services/syncbase/server/watchable/util.go
index cd37d3e..b77b15f 100644
--- a/services/syncbase/server/watchable/util.go
+++ b/services/syncbase/server/watchable/util.go
@@ -15,6 +15,7 @@
"v.io/syncbase/x/ref/services/syncbase/server/util"
"v.io/syncbase/x/ref/services/syncbase/store"
+ "v.io/v23/verror"
)
var rng *rand.Rand = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
@@ -71,3 +72,7 @@
func split(key string) []string {
return util.SplitKeyParts(key)
}
+
+func convertError(err error) error {
+ return verror.Convert(verror.IDAction{}, nil, err)
+}
diff --git a/services/syncbase/store/invalid_types.go b/services/syncbase/store/invalid_types.go
index 7d215b6..bb008e2 100644
--- a/services/syncbase/store/invalid_types.go
+++ b/services/syncbase/store/invalid_types.go
@@ -4,18 +4,21 @@
package store
-// InvalidSnapshot is a store.Snapshot for which all methods return errors.
+import (
+ "v.io/v23/verror"
+)
+
+// InvalidSnapshot is a Snapshot for which all methods return errors.
type InvalidSnapshot struct {
Error error // returned by all methods
}
-// InvalidStream is a store.Stream for which all methods return errors.
+// InvalidStream is a Stream for which all methods return errors.
type InvalidStream struct {
Error error // returned by all methods
}
-// InvalidTransaction is a store.Transaction for which all methods return
-// errors.
+// InvalidTransaction is a Transaction for which all methods return errors.
type InvalidTransaction struct {
Error error // returned by all methods
}
@@ -31,12 +34,12 @@
// Close implements the store.Snapshot interface.
func (s *InvalidSnapshot) Close() error {
- return WrapError(s.Error)
+ return convertError(s.Error)
}
// Get implements the store.StoreReader interface.
func (s *InvalidSnapshot) Get(key, valbuf []byte) ([]byte, error) {
- return valbuf, WrapError(s.Error)
+ return valbuf, convertError(s.Error)
}
// Scan implements the store.StoreReader interface.
@@ -64,7 +67,7 @@
// Err implements the store.Stream interface.
func (s *InvalidStream) Err() error {
- return WrapError(s.Error)
+ return convertError(s.Error)
}
// Cancel implements the store.Stream interface.
@@ -81,7 +84,7 @@
// Get implements the store.StoreReader interface.
func (tx *InvalidTransaction) Get(key, valbuf []byte) ([]byte, error) {
- return valbuf, WrapError(tx.Error)
+ return valbuf, convertError(tx.Error)
}
// Scan implements the store.StoreReader interface.
@@ -91,20 +94,27 @@
// Put implements the store.StoreWriter interface.
func (tx *InvalidTransaction) Put(key, value []byte) error {
- return WrapError(tx.Error)
+ return convertError(tx.Error)
}
// Delete implements the store.StoreWriter interface.
func (tx *InvalidTransaction) Delete(key []byte) error {
- return WrapError(tx.Error)
+ return convertError(tx.Error)
}
// Commit implements the store.Transaction interface.
func (tx *InvalidTransaction) Commit() error {
- return WrapError(tx.Error)
+ return convertError(tx.Error)
}
// Abort implements the store.Transaction interface.
func (tx *InvalidTransaction) Abort() error {
- return WrapError(tx.Error)
+ return convertError(tx.Error)
+}
+
+////////////////////////////////////////////////////////////
+// Internal helpers
+
+func convertError(err error) error {
+ return verror.Convert(verror.IDAction{}, nil, err)
}
diff --git a/services/syncbase/store/leveldb/db.go b/services/syncbase/store/leveldb/db.go
index 62250c6..eb8507c 100644
--- a/services/syncbase/store/leveldb/db.go
+++ b/services/syncbase/store/leveldb/db.go
@@ -31,18 +31,15 @@
writeOptions *C.leveldb_writeoptions_t
err error
- // TODO(rogulenko): decide whether we need to make a defensive copy of
- // keys/values used by transactions.
- // txmu protects transaction-related variables below. It is also held during
- // commit.
- // txmu must always be acquired before mu.
+ // txmu protects the transaction-related variables below, and is also held
+ // during transaction commits. It must always be acquired before mu.
txmu sync.Mutex
// txEvents is a queue of create/commit transaction events.
txEvents *list.List
txSequenceNumber uint64
// txTable is a set of keys written by recent transactions. This set
// includes all write sets of transactions committed after the oldest living
- // transaction.
+ // (in-flight) transaction.
txTable *trie
}
@@ -81,7 +78,7 @@
d.mu.Lock()
defer d.mu.Unlock()
if d.err != nil {
- return store.WrapError(d.err)
+ return convertError(d.err)
}
d.node.Close()
C.leveldb_close(d.cDb)
@@ -226,7 +223,7 @@
d.mu.RLock()
defer d.mu.RUnlock()
if d.err != nil {
- return valbuf, store.WrapError(d.err)
+ return valbuf, convertError(d.err)
}
var cError *C.char
var valLen C.size_t
diff --git a/services/syncbase/store/leveldb/snapshot.go b/services/syncbase/store/leveldb/snapshot.go
index 4bf59b6..e43d67f 100644
--- a/services/syncbase/store/leveldb/snapshot.go
+++ b/services/syncbase/store/leveldb/snapshot.go
@@ -49,7 +49,7 @@
s.mu.Lock()
defer s.mu.Unlock()
if s.err != nil {
- return store.WrapError(s.err)
+ return convertError(s.err)
}
s.node.Close()
C.leveldb_readoptions_destroy(s.cOpts)
@@ -65,7 +65,7 @@
s.mu.RLock()
defer s.mu.RUnlock()
if s.err != nil {
- return valbuf, store.WrapError(s.err)
+ return valbuf, convertError(s.err)
}
return s.d.getWithOpts(key, valbuf, s.cOpts)
}
diff --git a/services/syncbase/store/leveldb/stream.go b/services/syncbase/store/leveldb/stream.go
index 2840e5d..5775337 100644
--- a/services/syncbase/store/leveldb/stream.go
+++ b/services/syncbase/store/leveldb/stream.go
@@ -113,7 +113,7 @@
func (s *stream) Err() error {
s.mu.Lock()
defer s.mu.Unlock()
- return store.WrapError(s.err)
+ return convertError(s.err)
}
// Cancel implements the store.Stream interface.
diff --git a/services/syncbase/store/leveldb/transaction.go b/services/syncbase/store/leveldb/transaction.go
index 308b4da..be6ac4f 100644
--- a/services/syncbase/store/leveldb/transaction.go
+++ b/services/syncbase/store/leveldb/transaction.go
@@ -107,7 +107,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return valbuf, store.WrapError(tx.err)
+ return valbuf, convertError(tx.err)
}
tx.reads.keys = append(tx.reads.keys, key)
return tx.snapshot.Get(key, valbuf)
@@ -132,7 +132,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
tx.writes = append(tx.writes, writeOp{
t: putOp,
@@ -147,7 +147,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
tx.writes = append(tx.writes, writeOp{
t: deleteOp,
@@ -179,7 +179,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
tx.err = verror.New(verror.ErrBadState, nil, store.ErrMsgCommittedTxn)
// Explicitly remove this transaction from the event queue. If this was the
@@ -200,7 +200,7 @@
tx.mu.Lock()
defer tx.mu.Unlock()
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
tx.err = verror.New(verror.ErrCanceled, nil, store.ErrMsgAbortedTxn)
tx.close()
diff --git a/services/syncbase/store/leveldb/util.go b/services/syncbase/store/leveldb/util.go
index dce69bb..7d92527 100644
--- a/services/syncbase/store/leveldb/util.go
+++ b/services/syncbase/store/leveldb/util.go
@@ -44,3 +44,7 @@
})
return *(*[]byte)(ptr)
}
+
+func convertError(err error) error {
+ return verror.Convert(verror.IDAction{}, nil, err)
+}
diff --git a/services/syncbase/store/memstore/snapshot.go b/services/syncbase/store/memstore/snapshot.go
index 53bd6ef..43beffd 100644
--- a/services/syncbase/store/memstore/snapshot.go
+++ b/services/syncbase/store/memstore/snapshot.go
@@ -41,7 +41,7 @@
s.mu.Lock()
defer s.mu.Unlock()
if s.err != nil {
- return store.WrapError(s.err)
+ return convertError(s.err)
}
s.node.Close()
s.err = verror.New(verror.ErrCanceled, nil, store.ErrMsgClosedSnapshot)
@@ -53,7 +53,7 @@
s.mu.Lock()
defer s.mu.Unlock()
if s.err != nil {
- return valbuf, store.WrapError(s.err)
+ return valbuf, convertError(s.err)
}
value, ok := s.data[string(key)]
if !ok {
diff --git a/services/syncbase/store/memstore/store.go b/services/syncbase/store/memstore/store.go
index d24d5d1..7e3f816 100644
--- a/services/syncbase/store/memstore/store.go
+++ b/services/syncbase/store/memstore/store.go
@@ -39,7 +39,7 @@
st.mu.Lock()
defer st.mu.Unlock()
if st.err != nil {
- return store.WrapError(st.err)
+ return convertError(st.err)
}
st.node.Close()
st.err = verror.New(verror.ErrCanceled, nil, store.ErrMsgClosedStore)
@@ -51,7 +51,7 @@
st.mu.Lock()
defer st.mu.Unlock()
if st.err != nil {
- return valbuf, store.WrapError(st.err)
+ return valbuf, convertError(st.err)
}
value, ok := st.data[string(key)]
if !ok {
diff --git a/services/syncbase/store/memstore/stream.go b/services/syncbase/store/memstore/stream.go
index f7e5945..9ad89a6 100644
--- a/services/syncbase/store/memstore/stream.go
+++ b/services/syncbase/store/memstore/stream.go
@@ -85,7 +85,7 @@
func (s *stream) Err() error {
s.mu.Lock()
defer s.mu.Unlock()
- return store.WrapError(s.err)
+ return convertError(s.err)
}
// Cancel implements the store.Stream interface.
diff --git a/services/syncbase/store/memstore/transaction.go b/services/syncbase/store/memstore/transaction.go
index f7e99c4..888ce87 100644
--- a/services/syncbase/store/memstore/transaction.go
+++ b/services/syncbase/store/memstore/transaction.go
@@ -56,7 +56,7 @@
func (tx *transaction) error() error {
if tx.err != nil {
- return store.WrapError(tx.err)
+ return convertError(tx.err)
}
if tx.expired() {
return verror.New(verror.ErrBadState, nil, store.ErrMsgExpiredTxn)
diff --git a/services/syncbase/store/memstore/util.go b/services/syncbase/store/memstore/util.go
new file mode 100644
index 0000000..8dc3a7a
--- /dev/null
+++ b/services/syncbase/store/memstore/util.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package memstore
+
+import (
+ "v.io/v23/verror"
+)
+
+func convertError(err error) error {
+ return verror.Convert(verror.IDAction{}, nil, err)
+}
diff --git a/services/syncbase/store/model.go b/services/syncbase/store/model.go
index 1fcdd55..e30a5f0 100644
--- a/services/syncbase/store/model.go
+++ b/services/syncbase/store/model.go
@@ -6,6 +6,9 @@
// Currently, this API and its implementations are meant to be internal.
package store
+// TODO(sadovsky): Decide whether to defensively copy passed-in []byte's vs.
+// requiring clients not to modify passed-in []byte's.
+
// StoreReader reads data from a CRUD-capable storage engine.
type StoreReader interface {
// Get returns the value for the given key. The returned slice may be a
@@ -17,6 +20,7 @@
Get(key, valbuf []byte) ([]byte, error)
// Scan returns all rows with keys in range [start, limit).
+ // TODO(sadovsky): Describe the behavior of Scan with concurrent writes.
Scan(start, limit []byte) Stream
}
diff --git a/services/syncbase/store/util.go b/services/syncbase/store/util.go
index eee882a..d42a367 100644
--- a/services/syncbase/store/util.go
+++ b/services/syncbase/store/util.go
@@ -45,17 +45,3 @@
copy(dst, src)
return dst
}
-
-// WrapError wraps the given error with a verror. It is a no-op if the given
-// error is nil. The returned error contains the current stack trace, but
-// retains the input error's IDAction pair. If the given error is not a verror,
-// the IDAction pair of the returned error will be (ErrUnknown.ID, NoRetry).
-func WrapError(err error) error {
- if err == nil {
- return nil
- }
- return verror.New(verror.IDAction{
- verror.ErrorID(err),
- verror.Action(err),
- }, nil, err)
-}