syncbase: make storage engine somewhat configurable
Change-Id: I7ec6f794ca3c379050bbd6cfe91c431e6e630792
diff --git a/services/syncbase/server/app.go b/services/syncbase/server/app.go
index af92c09..aa4bfb3 100644
--- a/services/syncbase/server/app.go
+++ b/services/syncbase/server/app.go
@@ -5,6 +5,7 @@
package server
import (
+ "path"
"sync"
wire "v.io/syncbase/v23/services/syncbase"
@@ -148,7 +149,11 @@
if perms == nil {
perms = aData.Perms
}
- d, err := nosql.NewDatabase(ctx, call, a, dbName, perms)
+ d, err := nosql.NewDatabase(ctx, call, a, dbName, nosql.DatabaseOptions{
+ Perms: perms,
+ RootDir: path.Join(a.s.opts.RootDir, "apps", a.name, dbName),
+ Engine: a.s.opts.Engine,
+ })
if err != nil {
return err
}
diff --git a/services/syncbase/server/nosql/database.go b/services/syncbase/server/nosql/database.go
index 40cc85b..1f3cbd3 100644
--- a/services/syncbase/server/nosql/database.go
+++ b/services/syncbase/server/nosql/database.go
@@ -5,12 +5,13 @@
package nosql
import (
+ "path"
+
wire "v.io/syncbase/v23/services/syncbase/nosql"
"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/server/watchable"
"v.io/syncbase/x/ref/services/syncbase/store"
- "v.io/syncbase/x/ref/services/syncbase/store/memstore"
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security/access"
@@ -31,15 +32,27 @@
_ util.Layer = (*database)(nil)
)
+type DatabaseOptions struct {
+ // Database-level permissions.
+ Perms access.Permissions
+ // Root dir for data storage.
+ RootDir string
+ // Storage engine to use.
+ Engine string
+}
+
// 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, call rpc.ServerCall, a interfaces.App, name string, perms access.Permissions) (*database, error) {
- if perms == nil {
+func NewDatabase(ctx *context.T, call rpc.ServerCall, a interfaces.App, name string, opts DatabaseOptions) (*database, error) {
+ if opts.Perms == nil {
return nil, verror.New(verror.ErrInternal, ctx, "perms must be specified")
}
- // TODO(sadovsky): Make storage engine pluggable.
- st, err := watchable.Wrap(memstore.New(), &watchable.Options{
+ st, err := util.OpenStore(opts.Engine, path.Join(opts.RootDir, opts.Engine))
+ if err != nil {
+ return nil, err
+ }
+ st, err = watchable.Wrap(st, &watchable.Options{
ManagedPrefixes: []string{util.RowPrefix},
})
if err != nil {
@@ -52,7 +65,7 @@
}
data := &databaseData{
Name: d.name,
- Perms: perms,
+ Perms: opts.Perms,
}
if err := util.Put(ctx, call, d.st, d, data); err != nil {
return nil, err
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index b352a27..fd3e3b8 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -9,13 +9,13 @@
// preserve privacy.
import (
+ "path"
"sync"
wire "v.io/syncbase/v23/services/syncbase"
"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/syncbase/x/ref/services/syncbase/store/memstore"
"v.io/syncbase/x/ref/services/syncbase/vsync"
"v.io/v23/context"
"v.io/v23/rpc"
@@ -23,9 +23,19 @@
"v.io/v23/verror"
)
+type ServiceOptions struct {
+ // Service-level permissions.
+ Perms access.Permissions
+ // Root dir for data storage.
+ RootDir string
+ // Storage engine to use (for service and per-database engines).
+ Engine string
+}
+
type service struct {
st store.Store // keeps track of which apps and databases exist, etc.
sync interfaces.SyncServerMethods
+ opts ServiceOptions
// Guards the fields below. Held during app Create, Delete, and
// SetPermissions.
mu sync.Mutex
@@ -40,28 +50,28 @@
// NewService creates a new service instance and returns it.
// Returns a VDL-compatible error.
-func NewService(ctx *context.T, call rpc.ServerCall, perms access.Permissions) (*service, error) {
- if perms == nil {
+func NewService(ctx *context.T, call rpc.ServerCall, opts ServiceOptions) (*service, error) {
+ if opts.Perms == nil {
return nil, verror.New(verror.ErrInternal, ctx, "perms must be specified")
}
- // TODO(sadovsky): Make storage engine pluggable.
+ st, err := util.OpenStore(opts.Engine, path.Join(opts.RootDir, opts.Engine))
+ if err != nil {
+ return nil, err
+ }
s := &service{
- st: memstore.New(),
+ st: st,
+ opts: opts,
apps: map[string]*app{},
}
-
data := &serviceData{
- Perms: perms,
+ Perms: opts.Perms,
}
if err := util.Put(ctx, call, s.st, s, data); err != nil {
return nil, err
}
-
- var err error
if s.sync, err = vsync.New(ctx, call, s); err != nil {
return nil, err
}
-
return s, nil
}
diff --git a/services/syncbase/server/util/store_util.go b/services/syncbase/server/util/store_util.go
index 8e289d6..ebee090 100644
--- a/services/syncbase/server/util/store_util.go
+++ b/services/syncbase/server/util/store_util.go
@@ -5,9 +5,12 @@
package util
import (
+ "os"
"strconv"
"v.io/syncbase/x/ref/services/syncbase/store"
+ "v.io/syncbase/x/ref/services/syncbase/store/leveldb"
+ "v.io/syncbase/x/ref/services/syncbase/store/memstore"
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security/access"
@@ -104,7 +107,7 @@
}
////////////////////////////////////////////////////////////
-// RPC-oblivious, lower-level get/put
+// RPC-oblivious, lower-level helpers
func GetObject(st store.StoreReader, k string, v interface{}) error {
bytes, err := st.Get([]byte(k), nil)
@@ -121,3 +124,17 @@
}
return st.Put([]byte(k), bytes)
}
+
+func OpenStore(engine, path string) (store.Store, error) {
+ switch engine {
+ case "memstore":
+ return memstore.New(), nil
+ case "leveldb":
+ if err := os.MkdirAll(path, 0700); err != nil {
+ return nil, verror.New(verror.ErrInternal, nil, err)
+ }
+ return leveldb.Open(path)
+ default:
+ return nil, verror.New(verror.ErrBadArg, nil, engine)
+ }
+}
diff --git a/services/syncbase/syncbased/main.go b/services/syncbase/syncbased/main.go
index 44865a3..01f9070 100644
--- a/services/syncbase/syncbased/main.go
+++ b/services/syncbase/syncbased/main.go
@@ -23,7 +23,9 @@
)
var (
- name = flag.String("name", "", "Name to mount at.")
+ name = flag.String("name", "", "Name to mount at.")
+ rootDir = flag.String("root-dir", "/var/lib/syncbase", "Root dir for storage engines and other data")
+ engine = flag.String("engine", "memstore", "Storage engine to use. Currently supported: memstore and leveldb.")
)
// defaultPerms returns a permissions object that grants all permissions to the
@@ -62,7 +64,11 @@
perms = defaultPerms(security.DefaultBlessingPatterns(v23.GetPrincipal(ctx)))
}
- service, err := server.NewService(nil, nil, perms)
+ service, err := server.NewService(nil, nil, server.ServiceOptions{
+ Perms: perms,
+ RootDir: *rootDir,
+ Engine: *engine,
+ })
if err != nil {
vlog.Fatal("server.NewService() failed: ", err)
}