Merge "syncbase: simplify store_util and its clients"
diff --git a/services/syncbase/server/app.go b/services/syncbase/server/app.go
index a2b081a..48338f5 100644
--- a/services/syncbase/server/app.go
+++ b/services/syncbase/server/app.go
@@ -35,7 +35,6 @@
var (
_ wire.AppServerMethods = (*app)(nil)
_ interfaces.App = (*app)(nil)
- _ util.Layer = (*app)(nil)
)
////////////////////////////////////////
@@ -69,7 +68,7 @@
return nil, "", verror.New(verror.ErrNoExist, ctx, a.name)
}
data := &appData{}
- if err := util.Get(ctx, call, a.s.st, a, data); err != nil {
+ if err := util.GetWithAuth(ctx, call, a.s.st, a.stKey(), data); err != nil {
return nil, "", err
}
return data.Perms, util.FormatVersion(data.Version), nil
@@ -84,7 +83,7 @@
closeSnapshot := func() error {
return sn.Close()
}
- if err := util.Get(ctx, call, sn, a, &appData{}); err != nil {
+ if err := util.GetWithAuth(ctx, call, sn, a.stKey(), &appData{}); err != nil {
closeSnapshot()
return nil, err
}
@@ -150,7 +149,7 @@
aData := &appData{}
if err := store.RunInTransaction(a.s.st, func(st store.StoreReadWriter) error {
// Check appData perms.
- if err := util.Get(ctx, call, st, a, aData); err != nil {
+ if err := util.GetWithAuth(ctx, call, st, a.stKey(), aData); err != nil {
return err
}
// Check for "database already exists".
@@ -267,20 +266,17 @@
return d.SetPermsInternal(ctx, call, perms, version)
}
-////////////////////////////////////////
-// util.Layer methods
-
func (a *app) Name() string {
return a.name
}
-func (a *app) StKey() string {
- return util.JoinKeyParts(util.AppPrefix, a.stKeyPart())
-}
-
////////////////////////////////////////
// Internal helpers
+func (a *app) stKey() string {
+ return util.JoinKeyParts(util.AppPrefix, a.stKeyPart())
+}
+
func (a *app) stKeyPart() string {
return a.name
}
diff --git a/services/syncbase/server/db_info.go b/services/syncbase/server/db_info.go
index cbfcebd..39ffdc8 100644
--- a/services/syncbase/server/db_info.go
+++ b/services/syncbase/server/db_info.go
@@ -19,58 +19,30 @@
"v.io/v23/context"
)
-type dbInfoLayer struct {
- name string
- a *app
-}
-
-var (
- _ util.Layer = (*dbInfoLayer)(nil)
-)
-
-////////////////////////////////////////
-// dbInfoLayer util.Layer methods
-
-func (d *dbInfoLayer) Name() string {
- return d.name
-}
-
-func (d *dbInfoLayer) StKey() string {
- return util.JoinKeyParts(util.DbInfoPrefix, d.stKeyPart())
-}
-
-////////////////////////////////////////
-// Internal helpers
-
-func (d *dbInfoLayer) stKeyPart() string {
- return util.JoinKeyParts(d.a.stKeyPart(), d.name)
+func dbInfoStKey(a *app, dbName string) string {
+ return util.JoinKeyParts(util.DbInfoPrefix, a.stKeyPart(), dbName)
}
// getDbInfo reads data from the storage engine.
-// Returns a VDL-compatible error.
func (a *app) getDbInfo(ctx *context.T, st store.StoreReader, dbName string) (*dbInfo, error) {
info := &dbInfo{}
- if err := util.GetWithoutAuth(ctx, st, &dbInfoLayer{dbName, a}, info); err != nil {
+ if err := util.Get(ctx, st, dbInfoStKey(a, dbName), info); err != nil {
return nil, err
}
return info, nil
}
// putDbInfo writes data to the storage engine.
-// Returns a VDL-compatible error.
func (a *app) putDbInfo(ctx *context.T, st store.StoreWriter, dbName string, info *dbInfo) error {
- return util.Put(ctx, st, &dbInfoLayer{dbName, a}, info)
+ return util.Put(ctx, st, dbInfoStKey(a, dbName), info)
}
// delDbInfo deletes data from the storage engine.
-// Returns a VDL-compatible error.
func (a *app) delDbInfo(ctx *context.T, st store.StoreWriter, dbName string) error {
- return util.Delete(ctx, st, &dbInfoLayer{dbName, a})
+ return util.Delete(ctx, st, dbInfoStKey(a, dbName))
}
-// updateDbInfo performs a read-modify-write.
-// fn should "modify" v, and should return a VDL-compatible error.
-// Returns a VDL-compatible error.
+// updateDbInfo performs a read-modify-write. fn should "modify" v.
func (a *app) updateDbInfo(ctx *context.T, st store.StoreReadWriter, dbName string, fn func(info *dbInfo) error) error {
_ = st.(store.Transaction) // panics on failure, as desired
info, err := a.getDbInfo(ctx, st, dbName)
diff --git a/services/syncbase/server/db_info_test.go b/services/syncbase/server/db_info_test.go
index 47db158..7bc0870 100644
--- a/services/syncbase/server/db_info_test.go
+++ b/services/syncbase/server/db_info_test.go
@@ -8,23 +8,18 @@
"testing"
)
-type stk struct {
- appName string
- dbName string
- stkey string
-}
-
-var stTestKey stk = stk{"app1", "db1", "$dbInfo:app1:db1"}
-
-var dbinfo *dbInfoLayer = &dbInfoLayer{
- name: stTestKey.dbName,
- a: &app{
- name: stTestKey.appName,
- },
-}
-
func TestStKey(t *testing.T) {
- if stTestKey.stkey != dbinfo.StKey() {
- t.Errorf("dbInfoLayer stkey expected to be %q but found to be %q", stTestKey.stkey, dbinfo.StKey())
+ tests := []struct {
+ appName string
+ dbName string
+ stKey string
+ }{
+ {"app1", "db1", "$dbInfo:app1:db1"},
+ }
+ for _, test := range tests {
+ got, want := dbInfoStKey(&app{name: test.appName}, test.dbName), test.stKey
+ if got != want {
+ t.Errorf("wrong stKey: got %q, want %q", got, want)
+ }
}
}
diff --git a/services/syncbase/server/interfaces/app.go b/services/syncbase/server/interfaces/app.go
index 8ac9990..b1d5d4f 100644
--- a/services/syncbase/server/interfaces/app.go
+++ b/services/syncbase/server/interfaces/app.go
@@ -5,15 +5,12 @@
package interfaces
import (
- "v.io/syncbase/x/ref/services/syncbase/server/util"
-
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security/access"
)
// App is an internal interface to the app layer.
-// All methods return VDL-compatible errors.
type App interface {
// Service returns the service handle for this app.
Service() Service
@@ -33,5 +30,6 @@
// SetDatabasePerms sets the perms for the specified database.
SetDatabasePerms(ctx *context.T, call rpc.ServerCall, dbName string, perms access.Permissions, version string) error
- util.Layer
+ // Name returns the name of this app.
+ Name() string
}
diff --git a/services/syncbase/server/interfaces/database.go b/services/syncbase/server/interfaces/database.go
index 832a940..bf518a4 100644
--- a/services/syncbase/server/interfaces/database.go
+++ b/services/syncbase/server/interfaces/database.go
@@ -5,7 +5,6 @@
package interfaces
import (
- "v.io/syncbase/x/ref/services/syncbase/server/util"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
"v.io/v23/rpc"
@@ -13,7 +12,6 @@
)
// Database is an internal interface to the database layer.
-// All methods return VDL-compatible errors.
type Database interface {
// St returns the storage engine instance for this database.
St() store.Store
@@ -30,5 +28,6 @@
// Designed for use from within App.SetDatabasePerms.
SetPermsInternal(ctx *context.T, call rpc.ServerCall, perms access.Permissions, version string) error
- util.Layer
+ // Name returns the name of this database.
+ Name() string
}
diff --git a/services/syncbase/server/interfaces/service.go b/services/syncbase/server/interfaces/service.go
index 4505dce..ce665e2 100644
--- a/services/syncbase/server/interfaces/service.go
+++ b/services/syncbase/server/interfaces/service.go
@@ -11,7 +11,6 @@
)
// Service is an internal interface to the service layer.
-// All methods return VDL-compatible errors.
type Service interface {
// St returns the storage engine instance for this service.
St() store.Store
diff --git a/services/syncbase/server/nosql/database.go b/services/syncbase/server/nosql/database.go
index f12d437..62ad371 100644
--- a/services/syncbase/server/nosql/database.go
+++ b/services/syncbase/server/nosql/database.go
@@ -62,7 +62,6 @@
var (
_ wire.DatabaseServerMethods = (*databaseReq)(nil)
_ interfaces.Database = (*database)(nil)
- _ util.Layer = (*database)(nil)
)
// DatabaseOptions configures a database.
@@ -99,7 +98,6 @@
}
// NewDatabase creates a new database instance and returns it.
-// Returns a VDL-compatible error.
// Designed for use from within App.CreateNoSQLDatabase.
func NewDatabase(ctx *context.T, a interfaces.App, name string, opts DatabaseOptions) (*database, error) {
if opts.Perms == nil {
@@ -113,7 +111,7 @@
Name: d.name,
Perms: opts.Perms,
}
- if err := util.Put(ctx, d.st, d, data); err != nil {
+ if err := util.Put(ctx, d.st, d.stKey(), data); err != nil {
return nil, err
}
return d, nil
@@ -275,7 +273,7 @@
return nil, "", wire.NewErrBoundToBatch(ctx)
}
data := &databaseData{}
- if err := util.Get(ctx, call, d.st, d, data); err != nil {
+ if err := util.GetWithAuth(ctx, call, d.st, d.stKey(), data); err != nil {
return nil, "", err
}
return data.Perms, util.FormatVersion(data.Version), nil
@@ -293,7 +291,7 @@
closeSnapshot := func() error {
return sn.Close()
}
- if err := util.Get(ctx, call, sn, d, &databaseData{}); err != nil {
+ if err := util.GetWithAuth(ctx, call, sn, d.stKey(), &databaseData{}); err != nil {
closeSnapshot()
return nil, err
}
@@ -348,7 +346,7 @@
if !d.exists {
vlog.Fatalf("database %q does not exist", d.name)
}
- return util.Get(ctx, call, st, d, &databaseData{})
+ return util.GetWithAuth(ctx, call, st, d.stKey(), &databaseData{})
}
func (d *database) SetPermsInternal(ctx *context.T, call rpc.ServerCall, perms access.Permissions, version string) error {
@@ -357,7 +355,7 @@
}
return store.RunInTransaction(d.st, func(st store.StoreReadWriter) error {
data := &databaseData{}
- return util.Update(ctx, call, st, d, data, func() error {
+ return util.UpdateWithAuth(ctx, call, st, d.stKey(), data, func() error {
if err := util.CheckVersion(ctx, version, data.Version); err != nil {
return err
}
@@ -368,19 +366,12 @@
})
}
-////////////////////////////////////////
-// util.Layer methods
-
func (d *database) Name() string {
return d.name
}
-func (d *database) StKey() string {
- return util.DatabasePrefix
-}
-
////////////////////////////////////////
-// query_db implementations
+// query_db implementation
// Implement query_db's Database, Table and KeyValueStream interfaces.
type queryDb struct {
@@ -403,7 +394,7 @@
},
}
// Now that we have a table, we need to check permissions.
- if err := util.Get(db.ctx, db.call, db.st, tDb.req, &tableData{}); err != nil {
+ if err := util.GetWithAuth(db.ctx, db.call, db.st, tDb.req.stKey(), &tableData{}); err != nil {
return nil, err
}
return tDb, nil
@@ -505,6 +496,10 @@
////////////////////////////////////////
// Internal helpers
+func (d *database) stKey() string {
+ return util.DatabasePrefix
+}
+
func (d *databaseReq) batchReader() store.StoreReader {
if d.batchId == nil {
return nil
diff --git a/services/syncbase/server/nosql/row.go b/services/syncbase/server/nosql/row.go
index 542224a..88c08c7 100644
--- a/services/syncbase/server/nosql/row.go
+++ b/services/syncbase/server/nosql/row.go
@@ -21,7 +21,6 @@
var (
_ wire.RowServerMethods = (*rowReq)(nil)
- _ util.Layer = (*rowReq)(nil)
)
////////////////////////////////////////
@@ -73,41 +72,32 @@
}
////////////////////////////////////////
-// util.Layer methods
+// Internal helpers
-func (r *rowReq) Name() string {
- return r.key
-}
-
-func (r *rowReq) StKey() string {
+func (r *rowReq) stKey() string {
return util.JoinKeyParts(util.RowPrefix, r.stKeyPart())
}
-////////////////////////////////////////
-// Internal helpers
-
func (r *rowReq) stKeyPart() string {
return util.JoinKeyParts(r.t.stKeyPart(), r.key)
}
// checkAccess checks that this row's table exists in the database, and performs
// an authorization check.
-// Returns a VDL-compatible error.
func (r *rowReq) checkAccess(ctx *context.T, call rpc.ServerCall, st store.StoreReader) error {
return r.t.checkAccess(ctx, call, st, r.key)
}
// get reads data from the storage engine.
// Performs authorization check.
-// Returns a VDL-compatible error.
func (r *rowReq) get(ctx *context.T, call rpc.ServerCall, st store.StoreReader) ([]byte, error) {
if err := r.checkAccess(ctx, call, st); err != nil {
return nil, err
}
- value, err := st.Get([]byte(r.StKey()), nil)
+ value, err := st.Get([]byte(r.stKey()), nil)
if err != nil {
if verror.ErrorID(err) == store.ErrUnknownKey.ID {
- return nil, verror.New(verror.ErrNoExist, ctx, r.Name())
+ return nil, verror.New(verror.ErrNoExist, ctx, r.stKey())
}
return nil, verror.New(verror.ErrInternal, ctx, err)
}
@@ -116,12 +106,11 @@
// put writes data to the storage engine.
// Performs authorization check.
-// Returns a VDL-compatible error.
func (r *rowReq) put(ctx *context.T, call rpc.ServerCall, st store.StoreReadWriter, value []byte) error {
if err := r.checkAccess(ctx, call, st); err != nil {
return err
}
- if err := st.Put([]byte(r.StKey()), value); err != nil {
+ if err := st.Put([]byte(r.stKey()), value); err != nil {
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
@@ -129,12 +118,11 @@
// delete deletes data from the storage engine.
// Performs authorization check.
-// Returns a VDL-compatible error.
func (r *rowReq) delete(ctx *context.T, call rpc.ServerCall, st store.StoreReadWriter) error {
if err := r.checkAccess(ctx, call, st); err != nil {
return err
}
- if err := st.Delete([]byte(r.StKey())); err != nil {
+ if err := st.Delete([]byte(r.stKey())); err != nil {
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
diff --git a/services/syncbase/server/nosql/table.go b/services/syncbase/server/nosql/table.go
index 82a0cab..37e1fbe 100644
--- a/services/syncbase/server/nosql/table.go
+++ b/services/syncbase/server/nosql/table.go
@@ -25,7 +25,6 @@
var (
_ wire.TableServerMethods = (*tableReq)(nil)
- _ util.Layer = (*tableReq)(nil)
)
////////////////////////////////////////
@@ -38,11 +37,11 @@
return store.RunInTransaction(t.d.st, func(st store.StoreReadWriter) error {
// Check databaseData perms.
dData := &databaseData{}
- if err := util.Get(ctx, call, st, t.d, dData); err != nil {
+ if err := util.GetWithAuth(ctx, call, st, t.d.stKey(), dData); err != nil {
return err
}
// Check for "table already exists".
- if err := util.GetWithoutAuth(ctx, st, t, &tableData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
+ if err := util.Get(ctx, st, t.stKey(), &tableData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return err
}
@@ -57,7 +56,7 @@
Name: t.name,
Perms: perms,
}
- return util.Put(ctx, st, t, data)
+ return util.Put(ctx, st, t.stKey(), data)
})
}
@@ -67,14 +66,14 @@
}
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 err := util.GetWithAuth(ctx, call, st, t.stKey(), &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.
- return util.Delete(ctx, st, t)
+ return util.Delete(ctx, st, t.stKey())
})
}
@@ -199,7 +198,7 @@
}
if prefix == "" {
data := &tableData{}
- return util.Update(ctx, call, st, t, data, func() error {
+ return util.UpdateWithAuth(ctx, call, st, t.stKey(), data, func() error {
data.Perms = perms
return nil
})
@@ -224,10 +223,10 @@
stPrefixLimit := stPrefix + util.PrefixRangeLimitSuffix
prefixPerms = stPrefixPerms{Parent: parent, Perms: perms}
// Put the (prefix, perms) pair to the database.
- if err := util.PutObject(st, stPrefix, prefixPerms); err != nil {
+ if err := util.Put(ctx, st, stPrefix, prefixPerms); err != nil {
return err
}
- return util.PutObject(st, stPrefixLimit, prefixPerms)
+ return util.Put(ctx, st, stPrefixLimit, prefixPerms)
}
if t.d.batchId != nil {
if st, err := t.d.batchReadWriter(); err != nil {
@@ -315,19 +314,12 @@
}
////////////////////////////////////////
-// util.Layer methods
+// Internal helpers
-func (t *tableReq) Name() string {
- return t.name
-}
-
-func (t *tableReq) StKey() string {
+func (t *tableReq) stKey() string {
return util.JoinKeyParts(util.TablePrefix, t.stKeyPart())
}
-////////////////////////////////////////
-// Internal helpers
-
func (t *tableReq) stKeyPart() string {
return t.name
}
@@ -348,7 +340,7 @@
return verror.New(verror.ErrInternal, ctx, err)
}
prefixPerms.Parent = newParent
- if err := util.PutObject(st, string(key), prefixPerms); err != nil {
+ if err := util.Put(ctx, st, string(key), prefixPerms); err != nil {
it.Cancel()
return err
}
@@ -359,31 +351,29 @@
return nil
}
-// lock invalidates all concurrent transactions with ErrConcurrentTransaction
-// that have accessed this table.
-// Returns a VDL-compatible error.
+// lock invalidates all in-flight transactions that have touched this table,
+// such that any subsequent tx.Commit() will return ErrConcurrentTransaction.
//
-// It is necessary to call lock() every time prefix permissions are updated,
-// so snapshots inside all transactions reflect up-to-date permissions. Since
+// It is necessary to call lock() every time prefix permissions are updated so
+// that snapshots inside all transactions reflect up-to-date permissions. Since
// every public function that touches this table has to read the table-level
-// permissions object, it is enough to add the key of table-level permissions
-// to the write set of the current transaction.
+// permissions object, it suffices to add the key of this object to the write
+// set of the current transaction.
//
// TODO(rogulenko): Revisit this behavior to provide more granularity.
-// A possible option would be to add prefix and its parent to the write set
-// of the current transaction when permissions object for a prefix is updated.
+// One option is to add a prefix and its parent to the write set of the current
+// transaction when the permissions object for that prefix is updated.
func (t *tableReq) lock(ctx *context.T, st store.StoreReadWriter) error {
var data tableData
- if err := util.GetWithoutAuth(ctx, st, t, &data); err != nil {
+ if err := util.Get(ctx, st, t.stKey(), &data); err != nil {
return err
}
- return util.Put(ctx, st, t, data)
+ return util.Put(ctx, st, t.stKey(), data)
}
// checkAccess checks that this table exists in the database, and performs
// an authorization check. The access is checked at table level and at the
// level of the most specific prefix for the given key.
-// Returns a VDL-compatible error.
// TODO(rogulenko): Revisit this behavior. Eventually we'll want the table-level
// access check to be a check for "Resolve", i.e. also check access to
// service, app and database.
@@ -393,7 +383,7 @@
return err
}
if prefix != "" {
- if err := util.Get(ctx, call, st, t, &tableData{}); err != nil {
+ if err := util.GetWithAuth(ctx, call, st, t.stKey(), &tableData{}); err != nil {
return err
}
}
@@ -405,15 +395,14 @@
}
// permsForKey returns the longest prefix of the given key that has
-// associated permissions with its permissions object.
+// associated permissions, along with its permissions object.
// permsForKey doesn't perform an authorization check.
-// Returns a VDL-compatible error.
//
-// Virtually we represent all prefixes as a forest T, where each vertex maps to
-// a prefix. A parent for a string is the maximum proper prefix of it that
+// Effectively, we represent all prefixes as a forest T, where each vertex maps
+// to a prefix. A parent for a string is the maximum proper prefix of it that
// belongs to T. Each prefix P from T is represented as a pair of entries with
-// keys P and P~ with values of type stPrefixPerms (parent + perms).
-// High level of how this function works:
+// keys P and P~ with values of type stPrefixPerms (parent + perms). High level
+// explanation of how this function works:
// 1 iter = db.Scan(K, "")
// Here last character of iter.Key() is removed automatically if it is '~'
// 2 if hasPrefix(K, iter.Key()) return iter.Value()
@@ -452,17 +441,16 @@
// permsForPrefix returns the permissions object associated with the
// provided prefix.
-// Returns a VDL-compatible error.
func (t *tableReq) permsForPrefix(ctx *context.T, st store.StoreReader, prefix string) (stPrefixPerms, error) {
if prefix == "" {
var data tableData
- if err := util.GetWithoutAuth(ctx, st, t, &data); err != nil {
+ if err := util.Get(ctx, st, t.stKey(), &data); err != nil {
return stPrefixPerms{}, err
}
return stPrefixPerms{Perms: data.Perms}, nil
}
var prefixPerms stPrefixPerms
- if err := util.GetObject(st, t.prefixPermsKey(prefix), &prefixPerms); err != nil {
+ if err := util.Get(ctx, st, t.prefixPermsKey(prefix), &prefixPerms); err != nil {
return stPrefixPerms{}, verror.New(verror.ErrInternal, ctx, err)
}
return prefixPerms, nil
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index 9f9e5b3..8ed1480 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -39,7 +39,6 @@
var (
_ wire.ServiceServerMethods = (*service)(nil)
_ interfaces.Service = (*service)(nil)
- _ util.Layer = (*service)(nil)
)
// ServiceOptions configures a service.
@@ -56,7 +55,6 @@
}
// NewService creates a new service instance and returns it.
-// Returns a VDL-compatible error.
// TODO(sadovsky): If possible, close all stores when the server is stopped.
func NewService(ctx *context.T, call rpc.ServerCall, opts ServiceOptions) (*service, error) {
if opts.Perms == nil {
@@ -74,7 +72,7 @@
data := &serviceData{
Perms: opts.Perms,
}
- if err := util.GetWithoutAuth(ctx, st, s, &serviceData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
+ if err := util.Get(ctx, st, s.stKey(), &serviceData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return nil, err
}
@@ -125,7 +123,7 @@
}
} else {
// Service does not exist.
- if err := util.Put(ctx, st, s, data); err != nil {
+ if err := util.Put(ctx, st, s.stKey(), data); err != nil {
return nil, err
}
}
@@ -143,7 +141,7 @@
func (s *service) SetPermissions(ctx *context.T, call rpc.ServerCall, perms access.Permissions, version string) error {
return store.RunInTransaction(s.st, func(st store.StoreReadWriter) error {
data := &serviceData{}
- return util.Update(ctx, call, st, s, data, func() error {
+ return util.UpdateWithAuth(ctx, call, st, s.stKey(), data, func() error {
if err := util.CheckVersion(ctx, version, data.Version); err != nil {
return err
}
@@ -156,7 +154,7 @@
func (s *service) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
data := &serviceData{}
- if err := util.Get(ctx, call, s.st, s, data); err != nil {
+ if err := util.GetWithAuth(ctx, call, s.st, s.stKey(), data); err != nil {
return nil, "", err
}
return data.Perms, util.FormatVersion(data.Version), nil
@@ -168,7 +166,7 @@
closeSnapshot := func() error {
return sn.Close()
}
- if err := util.Get(ctx, call, sn, s, &serviceData{}); err != nil {
+ if err := util.GetWithAuth(ctx, call, sn, s.stKey(), &serviceData{}); err != nil {
closeSnapshot()
return nil, err
}
@@ -230,11 +228,11 @@
if err := store.RunInTransaction(s.st, func(st store.StoreReadWriter) error {
// Check serviceData perms.
sData := &serviceData{}
- if err := util.Get(ctx, call, st, s, sData); err != nil {
+ if err := util.GetWithAuth(ctx, call, st, s.stKey(), sData); err != nil {
return err
}
// Check for "app already exists".
- if err := util.GetWithoutAuth(ctx, st, a, &appData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
+ if err := util.Get(ctx, st, a.stKey(), &appData{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
if err != nil {
return err
}
@@ -248,7 +246,7 @@
Name: appName,
Perms: perms,
}
- return util.Put(ctx, st, a, data)
+ return util.Put(ctx, st, a.stKey(), data)
}); err != nil {
return err
}
@@ -267,14 +265,14 @@
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 err := util.GetWithAuth(ctx, call, st, a.stKey(), &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.
- return util.Delete(ctx, st, a)
+ return util.Delete(ctx, st, a.stKey())
}); err != nil {
return err
}
@@ -292,7 +290,7 @@
}
return store.RunInTransaction(s.st, func(st store.StoreReadWriter) error {
data := &appData{}
- return util.Update(ctx, call, st, a, data, func() error {
+ return util.UpdateWithAuth(ctx, call, st, a.stKey(), data, func() error {
if err := util.CheckVersion(ctx, version, data.Version); err != nil {
return err
}
@@ -304,12 +302,8 @@
}
////////////////////////////////////////
-// util.Layer methods
+// Other internal helpers
-func (s *service) Name() string {
- return "service"
-}
-
-func (s *service) StKey() string {
+func (s *service) stKey() string {
return util.ServicePrefix
}
diff --git a/services/syncbase/server/util/store_util.go b/services/syncbase/server/util/store_util.go
index a30f487..d62c222 100644
--- a/services/syncbase/server/util/store_util.go
+++ b/services/syncbase/server/util/store_util.go
@@ -29,100 +29,73 @@
return nil
}
-////////////////////////////////////////////////////////////
-// RPC-aware, higher-level get/put
-
-type Layer interface {
- // Name returns the name of this instance, e.g. "fooapp" or "bardb".
- Name() string
- // StKey returns the storage engine key to use for metadata about this layer,
- // e.g. "$table:baztable".
- StKey() string
-}
+// TODO(sadovsky): Perhaps these functions should strip key prefixes such as
+// "$table:" from the error messages they return.
type Permser interface {
// GetPerms returns the Permissions for this Layer.
GetPerms() access.Permissions
}
-// GetWithoutAuth does st.Get(l.StKey(), v), populating v.
-// Returns a VDL-compatible error.
-func GetWithoutAuth(ctx *context.T, st store.StoreReader, l Layer, v interface{}) error {
- if err := GetObject(st, l.StKey(), v); err != nil {
+// Get does st.Get(k, v) and wraps the returned error.
+func Get(ctx *context.T, st store.StoreReader, k string, v interface{}) error {
+ bytes, err := st.Get([]byte(k), nil)
+ if err != nil {
if verror.ErrorID(err) == store.ErrUnknownKey.ID {
- return verror.New(verror.ErrNoExist, ctx, l.Name())
+ return verror.New(verror.ErrNoExist, ctx, k)
}
return verror.New(verror.ErrInternal, ctx, err)
}
+ if err = vom.Decode(bytes, v); err != nil {
+ return verror.New(verror.ErrInternal, ctx, err)
+ }
return nil
}
-// Get does GetWithoutAuth followed by an auth check.
-// Returns a VDL-compatible error.
-func Get(ctx *context.T, call rpc.ServerCall, st store.StoreReader, l Layer, v Permser) error {
- if err := GetWithoutAuth(ctx, st, l, v); err != nil {
+// GetWithAuth does Get followed by an auth check.
+func GetWithAuth(ctx *context.T, call rpc.ServerCall, st store.StoreReader, k string, v Permser) error {
+ if err := Get(ctx, st, k, v); err != nil {
return err
}
auth, _ := access.PermissionsAuthorizer(v.GetPerms(), access.TypicalTagType())
if err := auth.Authorize(ctx, call.Security()); err != nil {
- return verror.New(verror.ErrNoAccess, ctx, l.Name())
+ return verror.New(verror.ErrNoAccess, ctx, err)
}
return nil
}
-// Put does st.Put(l.StKey(), v).
-// Returns a VDL-compatible error.
-// If you need to perform an authorization check, use Update().
-func Put(ctx *context.T, st store.StoreWriter, l Layer, v interface{}) error {
- if err := PutObject(st, l.StKey(), v); err != nil {
+// Put does st.Put(k, v) and wraps the returned error.
+func Put(ctx *context.T, st store.StoreWriter, k string, v interface{}) error {
+ bytes, err := vom.Encode(v)
+ if err != nil {
+ return verror.New(verror.ErrInternal, ctx, err)
+ }
+ if err = st.Put([]byte(k), bytes); err != nil {
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
}
-// Delete does st.Delete(l.StKey()).
-// Returns a VDL-compatible error.
-// If you need to perform an authorization check, call Get() first.
-func Delete(ctx *context.T, st store.StoreWriter, l Layer) error {
- if err := st.Delete([]byte(l.StKey())); err != nil {
+// Delete does st.Delete(k, v) and wraps the returned error.
+func Delete(ctx *context.T, st store.StoreWriter, k string) error {
+ if err := st.Delete([]byte(k)); err != nil {
return verror.New(verror.ErrInternal, ctx, err)
}
return nil
}
-// Update performs a read-modify-write.
-// Input v is populated by the "read" step. fn should "modify" v, and should
-// return a VDL-compatible error.
+// UpdateWithAuth performs a read-modify-write.
+// Input v is populated by the "read" step. fn should "modify" v.
// Performs an auth check as part of the "read" step.
-// Returns a VDL-compatible error.
-func Update(ctx *context.T, call rpc.ServerCall, st store.StoreReadWriter, l Layer, v Permser, fn func() error) error {
+func UpdateWithAuth(ctx *context.T, call rpc.ServerCall, st store.StoreReadWriter, k string, v Permser, fn func() error) error {
_ = st.(store.Transaction) // panics on failure, as desired
- if err := Get(ctx, call, st, l, v); err != nil {
+ if err := GetWithAuth(ctx, call, st, k, v); err != nil {
return err
}
if err := fn(); err != nil {
return err
}
- return Put(ctx, st, l, v)
-}
-
-////////////////////////////////////////////////////////////
-// RPC-oblivious, lower-level helpers
-
-func GetObject(st store.StoreReader, k string, v interface{}) error {
- bytes, err := st.Get([]byte(k), nil)
- if err != nil {
- return err
- }
- return vom.Decode(bytes, v)
-}
-
-func PutObject(st store.StoreWriter, k string, v interface{}) error {
- bytes, err := vom.Encode(v)
- if err != nil {
- return err
- }
- return st.Put([]byte(k), bytes)
+ return Put(ctx, st, k, v)
}
type OpenOptions struct {
diff --git a/services/syncbase/server/watchable/transaction.go b/services/syncbase/server/watchable/transaction.go
index d5206b1..9890b6a 100644
--- a/services/syncbase/server/watchable/transaction.go
+++ b/services/syncbase/server/watchable/transaction.go
@@ -81,7 +81,6 @@
if !tx.st.managesKey(key) {
return tx.itx.Put(key, value)
}
-
version, err := putVersioned(tx.itx, key, value)
if err != nil {
return err
@@ -99,12 +98,14 @@
}
var err error
if !tx.st.managesKey(key) {
- err = tx.itx.Delete(key)
- } else {
- err = deleteVersioned(tx.itx, key)
- tx.ops = append(tx.ops, &OpDelete{DeleteOp{Key: key}})
+ return tx.itx.Delete(key)
}
- return err
+ err = deleteVersioned(tx.itx, key)
+ if err != nil {
+ return err
+ }
+ tx.ops = append(tx.ops, &OpDelete{DeleteOp{Key: key}})
+ return nil
}
// Commit implements the store.Transaction interface.
@@ -132,7 +133,7 @@
FromSync: tx.fromSync,
Continued: i < len(tx.ops)-1,
}
- if err := util.PutObject(tx.itx, key, value); err != nil {
+ if err := util.Put(nil, tx.itx, key, value); err != nil {
return err
}
seq++
diff --git a/services/syncbase/vsync/dag.go b/services/syncbase/vsync/dag.go
index 72e8d91..c0d8efa 100644
--- a/services/syncbase/vsync/dag.go
+++ b/services/syncbase/vsync/dag.go
@@ -713,10 +713,7 @@
return verror.New(verror.ErrInternal, ctx, "invalid version", version)
}
- if err := util.PutObject(tx, nodeKey(oid, version), node); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, nodeKey(oid, version), node)
}
// getNode retrieves the DAG node entry for the given (oid, version).
@@ -727,8 +724,8 @@
var node dagNode
key := nodeKey(oid, version)
- if err := util.GetObject(st, key, &node); err != nil {
- return nil, translateError(ctx, err, key)
+ if err := util.Get(ctx, st, key, &node); err != nil {
+ return nil, err
}
return &node, nil
}
@@ -741,10 +738,7 @@
return verror.New(verror.ErrInternal, ctx, "invalid version", version)
}
- if err := tx.Delete([]byte(nodeKey(oid, version))); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Delete(ctx, tx, nodeKey(oid, version))
}
// hasNode returns true if the node (oid, version) exists in the DAG.
@@ -769,18 +763,15 @@
return verror.New(verror.ErrInternal, ctx, fmt.Errorf("invalid version: %s", version))
}
- if err := util.PutObject(tx, headKey(oid), version); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, headKey(oid), version)
}
// getHead retrieves the DAG object head.
func getHead(ctx *context.T, st store.StoreReader, oid string) (string, error) {
var version string
key := headKey(oid)
- if err := util.GetObject(st, key, &version); err != nil {
- return NoVersion, translateError(ctx, err, key)
+ if err := util.Get(ctx, st, key, &version); err != nil {
+ return NoVersion, err
}
return version, nil
}
@@ -788,11 +779,7 @@
// delHead deletes the DAG object head.
func delHead(ctx *context.T, tx store.StoreReadWriter, oid string) error {
_ = tx.(store.Transaction)
-
- if err := tx.Delete([]byte(headKey(oid))); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Delete(ctx, tx, headKey(oid))
}
// batchKey returns the key used to access the DAG batch info.
@@ -808,10 +795,7 @@
return verror.New(verror.ErrInternal, ctx, "invalid batch id", btid)
}
- if err := util.PutObject(tx, batchKey(btid), info); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, batchKey(btid), info)
}
// getBatch retrieves the DAG batch entry.
@@ -822,8 +806,8 @@
var info batchInfo
key := batchKey(btid)
- if err := util.GetObject(st, key, &info); err != nil {
- return nil, translateError(ctx, err, key)
+ if err := util.Get(ctx, st, key, &info); err != nil {
+ return nil, err
}
return &info, nil
}
@@ -836,10 +820,7 @@
return verror.New(verror.ErrInternal, ctx, "invalid batch id", btid)
}
- if err := tx.Delete([]byte(batchKey(btid))); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Delete(ctx, tx, batchKey(btid))
}
// getParentMap is a testing and debug helper function that returns for an
diff --git a/services/syncbase/vsync/sync.go b/services/syncbase/vsync/sync.go
index 6d787e1..d268843 100644
--- a/services/syncbase/vsync/sync.go
+++ b/services/syncbase/vsync/sync.go
@@ -19,7 +19,6 @@
"v.io/syncbase/x/ref/services/syncbase/server/interfaces"
"v.io/syncbase/x/ref/services/syncbase/server/util"
- "v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
"v.io/v23/rpc"
@@ -73,7 +72,6 @@
rng = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
rngLock sync.Mutex
_ interfaces.SyncServerMethods = (*syncService)(nil)
- _ util.Layer = (*syncService)(nil)
)
// rand64 generates an unsigned 64-bit pseudo-random number.
@@ -105,15 +103,15 @@
}
data := &syncData{}
- if err := util.GetObject(sv.St(), s.StKey(), data); err != nil {
- if verror.ErrorID(err) != store.ErrUnknownKey.ID {
- return nil, verror.New(verror.ErrInternal, ctx, err)
+ if err := util.Get(ctx, sv.St(), s.stKey(), data); err != nil {
+ if verror.ErrorID(err) != verror.ErrNoExist.ID {
+ return nil, err
}
// First invocation of vsync.New().
// TODO(sadovsky): Maybe move guid generation and storage to serviceData.
data.Id = rand64()
- if err := util.PutObject(sv.St(), s.StKey(), data); err != nil {
- return nil, verror.New(verror.ErrInternal, ctx, err)
+ if err := util.Put(ctx, sv.St(), s.stKey(), data); err != nil {
+ return nil, err
}
}
@@ -150,13 +148,6 @@
return &syncDatabase{db: db}
}
-////////////////////////////////////////
-// util.Layer methods.
-
-func (s *syncService) Name() string {
- return "sync"
-}
-
-func (s *syncService) StKey() string {
+func (s *syncService) stKey() string {
return util.SyncPrefix
}
diff --git a/services/syncbase/vsync/sync_state.go b/services/syncbase/vsync/sync_state.go
index 8db0bc6..9f8bf17 100644
--- a/services/syncbase/vsync/sync_state.go
+++ b/services/syncbase/vsync/sync_state.go
@@ -274,17 +274,14 @@
// putDbSyncState persists the sync state object for a given Database.
func putDbSyncState(ctx *context.T, tx store.StoreReadWriter, ds *dbSyncState) error {
_ = tx.(store.Transaction)
- if err := util.PutObject(tx, dbSyncStateKey(), ds); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, dbSyncStateKey(), ds)
}
// getDbSyncState retrieves the sync state object for a given Database.
func getDbSyncState(ctx *context.T, st store.StoreReader) (*dbSyncState, error) {
var ds dbSyncState
- if err := util.GetObject(st, dbSyncStateKey(), &ds); err != nil {
- return nil, translateError(ctx, err, dbSyncStateKey())
+ if err := util.Get(ctx, st, dbSyncStateKey(), &ds); err != nil {
+ return nil, err
}
return &ds, nil
}
@@ -324,7 +321,9 @@
func hasLogRec(st store.StoreReader, id, gen uint64) bool {
// TODO(hpucha): optimize to avoid the unneeded fetch/decode of the data.
var rec localLogRec
- if err := util.GetObject(st, logRecKey(id, gen), &rec); err != nil {
+ // NOTE(sadovsky): This implementation doesn't explicitly handle
+ // non-ErrNoExist errors. Is that intentional?
+ if err := util.Get(nil, st, logRecKey(id, gen), &rec); err != nil {
return false
}
return true
@@ -333,17 +332,14 @@
// putLogRec stores the log record.
func putLogRec(ctx *context.T, tx store.StoreReadWriter, rec *localLogRec) error {
_ = tx.(store.Transaction)
- if err := util.PutObject(tx, logRecKey(rec.Metadata.Id, rec.Metadata.Gen), rec); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, logRecKey(rec.Metadata.Id, rec.Metadata.Gen), rec)
}
// getLogRec retrieves the log record for a given (devid, gen).
func getLogRec(ctx *context.T, st store.StoreReader, id, gen uint64) (*localLogRec, error) {
var rec localLogRec
- if err := util.GetObject(st, logRecKey(id, gen), &rec); err != nil {
- return nil, translateError(ctx, err, logRecKey(id, gen))
+ if err := util.Get(ctx, st, logRecKey(id, gen), &rec); err != nil {
+ return nil, err
}
return &rec, nil
}
@@ -351,9 +347,5 @@
// delLogRec deletes the log record for a given (devid, gen).
func delLogRec(ctx *context.T, tx store.StoreReadWriter, id, gen uint64) error {
_ = tx.(store.Transaction)
-
- if err := tx.Delete([]byte(logRecKey(id, gen))); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Delete(ctx, tx, logRecKey(id, gen))
}
diff --git a/services/syncbase/vsync/syncgroup.go b/services/syncbase/vsync/syncgroup.go
index 4045416..4b6512c 100644
--- a/services/syncbase/vsync/syncgroup.go
+++ b/services/syncbase/vsync/syncgroup.go
@@ -302,7 +302,9 @@
func hasSGDataEntry(st store.StoreReader, gid interfaces.GroupId) bool {
// TODO(rdaoud): optimize to avoid the unneeded fetch/decode of the data.
var sg interfaces.SyncGroup
- if err := util.GetObject(st, sgDataKey(gid), &sg); err != nil {
+ // NOTE(sadovsky): This implementation doesn't explicitly handle
+ // non-ErrNoExist errors. Is that intentional?
+ if err := util.Get(nil, st, sgDataKey(gid), &sg); err != nil {
return false
}
return true
@@ -312,7 +314,9 @@
func hasSGNameEntry(st store.StoreReader, name string) bool {
// TODO(rdaoud): optimize to avoid the unneeded fetch/decode of the data.
var gid interfaces.GroupId
- if err := util.GetObject(st, sgNameKey(name), &gid); err != nil {
+ // NOTE(sadovsky): This implementation doesn't explicitly handle
+ // non-ErrNoExist errors. Is that intentional?
+ if err := util.Get(nil, st, sgNameKey(name), &gid); err != nil {
return false
}
return true
@@ -321,28 +325,20 @@
// setSGDataEntry stores the SyncGroup data entry.
func setSGDataEntry(ctx *context.T, tx store.StoreReadWriter, gid interfaces.GroupId, sg *interfaces.SyncGroup) error {
_ = tx.(store.Transaction)
-
- if err := util.PutObject(tx, sgDataKey(gid), sg); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, sgDataKey(gid), sg)
}
// setSGNameEntry stores the SyncGroup name entry.
func setSGNameEntry(ctx *context.T, tx store.StoreReadWriter, name string, gid interfaces.GroupId) error {
_ = tx.(store.Transaction)
-
- if err := util.PutObject(tx, sgNameKey(name), gid); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, sgNameKey(name), gid)
}
// getSGDataEntry retrieves the SyncGroup data for a given group ID.
func getSGDataEntry(ctx *context.T, st store.StoreReader, gid interfaces.GroupId) (*interfaces.SyncGroup, error) {
var sg interfaces.SyncGroup
- if err := util.GetObject(st, sgDataKey(gid), &sg); err != nil {
- return nil, verror.New(verror.ErrInternal, ctx, err)
+ if err := util.Get(ctx, st, sgDataKey(gid), &sg); err != nil {
+ return nil, err
}
return &sg, nil
}
@@ -350,8 +346,8 @@
// getSGNameEntry retrieves the SyncGroup name to ID mapping.
func getSGNameEntry(ctx *context.T, st store.StoreReader, name string) (interfaces.GroupId, error) {
var gid interfaces.GroupId
- if err := util.GetObject(st, sgNameKey(name), &gid); err != nil {
- return gid, verror.New(verror.ErrNoExist, ctx, err)
+ if err := util.Get(ctx, st, sgNameKey(name), &gid); err != nil {
+ return gid, err
}
return gid, nil
}
@@ -359,21 +355,13 @@
// delSGDataEntry deletes the SyncGroup data entry.
func delSGDataEntry(ctx *context.T, tx store.StoreReadWriter, gid interfaces.GroupId) error {
_ = tx.(store.Transaction)
-
- if err := tx.Delete([]byte(sgDataKey(gid))); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Delete(ctx, tx, sgDataKey(gid))
}
// delSGNameEntry deletes the SyncGroup name to ID mapping.
func delSGNameEntry(ctx *context.T, tx store.StoreReadWriter, name string) error {
_ = tx.(store.Transaction)
-
- if err := tx.Delete([]byte(sgNameKey(name))); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Delete(ctx, tx, sgNameKey(name))
}
////////////////////////////////////////////////////////////
@@ -382,7 +370,6 @@
// TODO(hpucha): Pass blessings along.
func (sd *syncDatabase) CreateSyncGroup(ctx *context.T, call rpc.ServerCall, sgName string, spec wire.SyncGroupSpec, myInfo wire.SyncGroupMemberInfo) error {
err := store.RunInTransaction(sd.db.St(), func(tx store.StoreReadWriter) error {
-
// Check permissions on Database.
if err := sd.db.CheckPermsInternal(ctx, call, tx); err != nil {
return err
diff --git a/services/syncbase/vsync/util.go b/services/syncbase/vsync/util.go
index 65d47eb..3622672 100644
--- a/services/syncbase/vsync/util.go
+++ b/services/syncbase/vsync/util.go
@@ -12,7 +12,6 @@
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
"v.io/v23/rpc"
- "v.io/v23/verror"
"v.io/x/lib/vlog"
)
@@ -75,14 +74,6 @@
return db.St(), nil
}
-// translateError translates store errors.
-func translateError(ctx *context.T, err error, key string) error {
- if verror.ErrorID(err) == store.ErrUnknownKey.ID {
- return verror.New(verror.ErrNoExist, ctx, key)
- }
- return verror.New(verror.ErrInternal, ctx, key, err)
-}
-
// unixNanoToTime converts a Unix timestamp in nanoseconds to a Time object.
func unixNanoToTime(timestamp int64) time.Time {
if timestamp < 0 {
diff --git a/services/syncbase/vsync/watcher.go b/services/syncbase/vsync/watcher.go
index 4865d82..f21dfa6 100644
--- a/services/syncbase/vsync/watcher.go
+++ b/services/syncbase/vsync/watcher.go
@@ -437,19 +437,15 @@
// setResMark stores the watcher resume marker for a database.
func setResMark(ctx *context.T, tx store.StoreReadWriter, resMark string) error {
_ = tx.(store.Transaction)
-
- if err := util.PutObject(tx, resMarkKey(), resMark); err != nil {
- return verror.New(verror.ErrInternal, ctx, err)
- }
- return nil
+ return util.Put(ctx, tx, resMarkKey(), resMark)
}
// getResMark retrieves the watcher resume marker for a database.
func getResMark(ctx *context.T, st store.StoreReader) (string, error) {
var resMark string
key := resMarkKey()
- if err := util.GetObject(st, key, &resMark); err != nil {
- return NoVersion, translateError(ctx, err, key)
+ if err := util.Get(ctx, st, key, &resMark); err != nil {
+ return NoVersion, err
}
return resMark, nil
}