veyron/lib/stats: Let Glob return inner nodes
With this change, Glob will include inner nodes that don't have a value
when includeValues is false. This means that Glob can be used to
traverse the tree one level at a time. WatchGlob will return all nodes
on the first cycle, and then only the nodes that change afterwards.
Change-Id: I221b4f4672992b3173810c7e6a4b63985eaba1b1
diff --git a/lib/stats/glob.go b/lib/stats/glob.go
index 2ba7cb7..c630e1e 100644
--- a/lib/stats/glob.go
+++ b/lib/stats/glob.go
@@ -37,12 +37,14 @@
// globStepLocked applies a glob recursively.
func globStepLocked(prefix string, g *glob.Glob, n *node, updatedSince time.Time, includeValues bool, result *[]KeyValue) {
- if g.Len() == 0 && n.object != nil && (updatedSince.IsZero() || !n.object.LastUpdate().Before(updatedSince)) {
- var v interface{}
- if includeValues {
- v = n.object.Value()
+ if g.Len() == 0 {
+ if updatedSince.IsZero() || (n.object != nil && !n.object.LastUpdate().Before(updatedSince)) {
+ var v interface{}
+ if includeValues && n.object != nil {
+ v = n.object.Value()
+ }
+ *result = append(*result, KeyValue{prefix, v})
}
- *result = append(*result, KeyValue{prefix, v})
}
if g.Finished() {
return
diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go
index 9d24b8c..37a68e0 100644
--- a/lib/stats/stats_test.go
+++ b/lib/stats/stats_test.go
@@ -13,8 +13,8 @@
"veyron2/rt"
)
-func doGlob(root, pattern string, since time.Time) ([]libstats.KeyValue, error) {
- it := libstats.Glob(root, pattern, since, true)
+func doGlob(root, pattern string, since time.Time, includeValues bool) ([]libstats.KeyValue, error) {
+ it := libstats.Glob(root, pattern, since, includeValues)
out := []libstats.KeyValue{}
for it.Advance() {
v := it.Value()
@@ -87,7 +87,8 @@
t.Errorf("unexpected result. Got %v, want %v", got, expected)
}
- result, err := doGlob("", "...", now)
+ // Glob showing only nodes with a value.
+ result, err := doGlob("", "...", now, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -110,7 +111,7 @@
t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
}
- result, err = doGlob("", "ipc/test/*", now)
+ result, err = doGlob("", "ipc/test/*", now, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -124,10 +125,33 @@
t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
}
+ // Glob showing all nodes without values
+ result, err = doGlob("", "ipc/...", time.Time{}, false)
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ expected = []libstats.KeyValue{
+ libstats.KeyValue{Key: "ipc"},
+ libstats.KeyValue{Key: "ipc/test"},
+ libstats.KeyValue{Key: "ipc/test/aaa"},
+ libstats.KeyValue{Key: "ipc/test/bbb"},
+ libstats.KeyValue{Key: "ipc/test/ccc"},
+ libstats.KeyValue{Key: "ipc/test/ddd"},
+ libstats.KeyValue{Key: "ipc/test/ddd/delta10m"},
+ libstats.KeyValue{Key: "ipc/test/ddd/delta1h"},
+ libstats.KeyValue{Key: "ipc/test/ddd/delta1m"},
+ libstats.KeyValue{Key: "ipc/test/ddd/rate10m"},
+ libstats.KeyValue{Key: "ipc/test/ddd/rate1h"},
+ libstats.KeyValue{Key: "ipc/test/ddd/rate1m"},
+ }
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
+ }
+
// Test the rate counter.
now = now.Add(10 * time.Second)
d.Incr(100)
- result, err = doGlob("", "ipc/test/ddd/*", now)
+ result, err = doGlob("", "ipc/test/ddd/*", now, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -144,7 +168,7 @@
}
// Test Glob on non-root object.
- result, err = doGlob("ipc/test", "*", time.Time{})
+ result, err = doGlob("ipc/test", "*", time.Time{}, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -158,7 +182,7 @@
t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
}
- result, err = doGlob("ipc/test/aaa", "", time.Time{})
+ result, err = doGlob("ipc/test/aaa", "", time.Time{}, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -170,7 +194,7 @@
}
// Test LastUpdate. The test only works on Counters.
- result, err = doGlob("ipc/test", "ddd", now)
+ result, err = doGlob("ipc/test", "ddd", now, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -181,7 +205,7 @@
t.Errorf("unexpected result. Got %#v, want %#v", result, expected)
}
- result, err = doGlob("ipc/test", "ddd", now.Add(time.Second))
+ result, err = doGlob("ipc/test", "ddd", now.Add(time.Second), true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -195,7 +219,7 @@
h.Add(1)
h.Add(2)
- result, err = doGlob("", "ipc/test/hhh", now)
+ result, err = doGlob("", "ipc/test/hhh", now, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -224,7 +248,7 @@
now = now.Add(30 * time.Second)
h.Add(3)
- result, err = doGlob("", "ipc/test/hhh/delta1m", now)
+ result, err = doGlob("", "ipc/test/hhh/delta1m", now, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
diff --git a/services/mgmt/stats/impl/stats_invoker_test.go b/services/mgmt/stats/impl/stats_invoker_test.go
index c06e0ee..46e751e 100644
--- a/services/mgmt/stats/impl/stats_invoker_test.go
+++ b/services/mgmt/stats/impl/stats_invoker_test.go
@@ -92,6 +92,7 @@
t.Errorf("gstream.Finish failed: %v", err)
}
expected := []string{
+ "testing/foo",
"testing/foo/bar",
"testing/foo/bar/delta10m",
"testing/foo/bar/delta1h",