syncbase/server: Implement GlobChildren__ instead of Glob__.
The client-side Glob implementation is now simpler, since it can use the
namespace.Glob method directly.
GlobChildren__ is much simpler than Glob__, and gives us (almost)
exactly the behavior we want. It can be problematic when a name has
lots of children, because all the children must be returned by
GlobChildren__, and filtering happens at a higher level. We should
consider passing a pattern to GlobChildren__ so that it can choose to do
its own filtering to save work.
Change-Id: I094b07cc039fcfd9c14d0e92ef2e7ea1223be80f
diff --git a/services/syncbase/server/app.go b/services/syncbase/server/app.go
index 81276f0..2151b1a 100644
--- a/services/syncbase/server/app.go
+++ b/services/syncbase/server/app.go
@@ -13,7 +13,6 @@
"v.io/syncbase/x/ref/services/syncbase/server/util"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
- "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -63,13 +62,14 @@
return data.Perms, util.FormatVersion(data.Version), nil
}
-func (a *app) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
+func (a *app) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
// Check perms.
sn := a.s.st.NewSnapshot()
if err := util.Get(ctx, call, sn, a, &appData{}); err != nil {
sn.Close()
return nil, err
}
+ pattern := "*"
return util.Glob(ctx, call, pattern, sn, util.JoinKeyParts(util.DbInfoPrefix, a.name))
}
diff --git a/services/syncbase/server/nosql/database.go b/services/syncbase/server/nosql/database.go
index 94664fb..c69af62 100644
--- a/services/syncbase/server/nosql/database.go
+++ b/services/syncbase/server/nosql/database.go
@@ -12,7 +12,6 @@
"v.io/syncbase/x/ref/services/syncbase/store/memstore"
"v.io/syncbase/x/ref/services/syncbase/store/watchable"
"v.io/v23/context"
- "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -97,13 +96,14 @@
return data.Perms, util.FormatVersion(data.Version), nil
}
-func (d *database) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
+func (d *database) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
// Check perms.
sn := d.st.NewSnapshot()
if err := util.Get(ctx, call, sn, d, &databaseData{}); err != nil {
sn.Close()
return nil, err
}
+ pattern := "*"
return util.Glob(ctx, call, pattern, sn, util.TablePrefix)
}
diff --git a/services/syncbase/server/nosql/table.go b/services/syncbase/server/nosql/table.go
index edfd195..ca89bcf1 100644
--- a/services/syncbase/server/nosql/table.go
+++ b/services/syncbase/server/nosql/table.go
@@ -9,7 +9,6 @@
"v.io/syncbase/x/ref/services/syncbase/server/util"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
- "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -117,13 +116,14 @@
return verror.NewErrNotImplemented(ctx)
}
-func (t *table) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
+func (t *table) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
// Check perms.
sn := t.d.st.NewSnapshot()
if err := util.Get(ctx, call, sn, t, &tableData{}); err != nil {
sn.Close()
return nil, err
}
+ pattern := "*"
return util.Glob(ctx, call, pattern, sn, util.JoinKeyParts(util.RowPrefix, t.name))
}
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index 68a72ab..ce2c63e 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -14,7 +14,6 @@
"v.io/syncbase/x/ref/services/syncbase/store/memstore"
"v.io/syncbase/x/ref/services/syncbase/vsync"
"v.io/v23/context"
- "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -87,13 +86,14 @@
return data.Perms, util.FormatVersion(data.Version), nil
}
-func (s *service) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
+func (s *service) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
// Check perms.
sn := s.st.NewSnapshot()
if err := util.Get(ctx, call, sn, s, &serviceData{}); err != nil {
sn.Close()
return nil, err
}
+ pattern := "*"
return util.Glob(ctx, call, pattern, sn, util.AppPrefix)
}
diff --git a/services/syncbase/server/util/glob.go b/services/syncbase/server/util/glob.go
index fe78670..0feb974 100644
--- a/services/syncbase/server/util/glob.go
+++ b/services/syncbase/server/util/glob.go
@@ -9,13 +9,28 @@
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
- "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/verror"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/glob"
)
+// NOTE(nlacasse): Syncbase handles Glob requests by implementing
+// GlobChildren__ at each level (service, app, database, table).
+//
+// Each GlobChildren__ method returns all the children of the name without
+// filtering them in any way. Any filtering that needs to happen based on the
+// glob pattern happens magically at a higher level. This results in a simple
+// GlobChildren__ implementation, but often an inefficient one, since we must
+// return every single child name, even though many will be filtered before
+// being sent to the client.
+//
+// We should consider changing GlobChildren__ so that it takes the pattern that
+// is being Globbed as an argument. Then, GlobChildren__ could be smarter about
+// what it returns, and only return names that already match the pattern. This
+// would prevent us from having to read all child names from the database every
+// time we Glob.
+
// globPatternToPrefix takes "foo*" and returns "foo".
// It assumes the input pattern is a valid glob pattern, and returns
// verror.ErrBadArg if the input is not a valid prefix.
@@ -42,7 +57,7 @@
// TODO(sadovsky): It sucks that Glob must be implemented differently from other
// streaming RPC handlers. I don't have much confidence that I've implemented
// both types of streaming correctly.
-func Glob(ctx *context.T, call rpc.ServerCall, pattern string, sn store.Snapshot, stKeyPrefix string) (<-chan naming.GlobReply, error) {
+func Glob(ctx *context.T, call rpc.ServerCall, pattern string, sn store.Snapshot, stKeyPrefix string) (<-chan string, error) {
// TODO(sadovsky): Support glob with non-prefix pattern.
if _, err := glob.Parse(pattern); err != nil {
sn.Close()
@@ -57,7 +72,7 @@
return nil, verror.New(verror.ErrInternal, ctx, err)
}
it := sn.Scan(ScanPrefixArgs(stKeyPrefix, prefix))
- ch := make(chan naming.GlobReply)
+ ch := make(chan string)
go func() {
defer sn.Close()
defer close(ch)
@@ -65,13 +80,14 @@
for it.Advance() {
key = it.Key(key)
parts := SplitKeyParts(string(key))
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: parts[len(parts)-1]}}
+ ch <- parts[len(parts)-1]
}
if err := it.Err(); err != nil {
+ // TODO(nlacasse): We should do something with the error other than
+ // just logging. Ideally we could send the error back to the
+ // client, but the GlobChildren__ API doesn't really allow for that
+ // since it uses a channel of strings rather than GlobResults.
vlog.VI(1).Infof("Glob(%q) failed: %v", pattern, err)
- ch <- naming.GlobReplyError{naming.GlobError{
- Error: verror.New(verror.ErrInternal, ctx, err),
- }}
}
}()
return ch, nil