services/mounttable/mounttablelib: Add num-mounted-servers
This change adds a counter for the total number of mounted servers. The
name of the stats object is mounttable/num-mounted-servers.
Partly addresses https://github.com/veyron/release-issues/issues/1896
Change-Id: I5b8e58a3d561c2e28998799971d674bbe03a2f4b
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index 8140ef8..ca9f9f1 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -53,9 +53,10 @@
// mountTable represents a namespace. One exists per server instance.
type mountTable struct {
- root *node
- superUsers access.AccessList
- nodeCounter *stats.Integer
+ root *node
+ superUsers access.AccessList
+ nodeCounter *stats.Integer
+ serverCounter *stats.Integer
}
var _ rpc.Dispatcher = (*mountTable)(nil)
@@ -99,8 +100,9 @@
// statsPrefix is the prefix for for exported statistics objects.
func NewMountTableDispatcher(aclfile, statsPrefix string) (rpc.Dispatcher, error) {
mt := &mountTable{
- root: new(node),
- nodeCounter: stats.NewInteger(naming.Join(statsPrefix, "num-nodes")),
+ root: new(node),
+ nodeCounter: stats.NewInteger(naming.Join(statsPrefix, "num-nodes")),
+ serverCounter: stats.NewInteger(naming.Join(statsPrefix, "num-mounted-servers")),
}
mt.root.parent = mt.newNode() // just for its lock
if err := mt.parseAccessLists(aclfile); err != nil && !os.IsNotExist(err) {
@@ -125,19 +127,22 @@
if n == nil {
return
}
- count := int64(0)
+ nodeCount := int64(0)
+ serverCount := int64(0)
queue := []*node{n}
for len(queue) > 0 {
- count++
n := queue[0]
queue = queue[1:]
+ nodeCount++
+ serverCount += numServers(n)
for _, ch := range n.children {
ch.Lock() // Keep locked until it is deleted.
queue = append(queue, ch)
}
}
- mt.nodeCounter.Incr(-count)
+ mt.nodeCounter.Incr(-nodeCount)
+ mt.serverCounter.Incr(-serverCount)
delete(parent.children, child)
}
@@ -471,6 +476,13 @@
return (flags & naming.Replace) == naming.Replace
}
+func numServers(n *node) int64 {
+ if n == nil || n.mount == nil || n.mount.servers == nil {
+ return 0
+ }
+ return int64(n.mount.servers.len())
+}
+
// Mount a server onto the name in the receiver.
func (ms *mountContext) Mount(ctx *context.T, call rpc.ServerCall, server string, ttlsecs uint32, flags naming.MountFlag) error {
mt := ms.mt
@@ -500,14 +512,10 @@
// We don't need the parent lock
n.parent.Unlock()
defer n.Unlock()
- if hasReplaceFlag(flags) {
- n.mount = nil
- }
+
wantMT := hasMTFlag(flags)
wantLeaf := hasLeafFlag(flags)
- if n.mount == nil {
- n.mount = &mount{servers: newServerList(), mt: wantMT, leaf: wantLeaf}
- } else {
+ if n.mount != nil {
if wantMT != n.mount.mt {
return verror.New(errMTDoesntMatch, ctx)
}
@@ -515,7 +523,15 @@
return verror.New(errLeafDoesntMatch, ctx)
}
}
+ nServersBefore := numServers(n)
+ if hasReplaceFlag(flags) {
+ n.mount = nil
+ }
+ if n.mount == nil {
+ n.mount = &mount{servers: newServerList(), mt: wantMT, leaf: wantLeaf}
+ }
n.mount.servers.add(server, time.Duration(ttlsecs)*time.Second)
+ mt.serverCounter.Incr(numServers(n) - nServersBefore)
return nil
}
@@ -581,11 +597,13 @@
if n == nil {
return nil
}
+ nServersBefore := numServers(n)
if server == "" {
n.mount = nil
} else if n.mount != nil && n.mount.servers.remove(server) == 0 {
n.mount = nil
}
+ mt.serverCounter.Incr(numServers(n) - nServersBefore)
removed := n.removeUseless(mt)
n.parent.Unlock()
n.Unlock()
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index fdb08c2..88dcd40 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -601,21 +601,31 @@
}
}
-func nodeCount(t *testing.T, ctx *context.T, addr string) int64 {
- st := stats.StatsClient(naming.JoinAddressName(addr, "__debug/stats/mounttable/num-nodes"))
+func getCounter(t *testing.T, ctx *context.T, name string) int64 {
+ st := stats.StatsClient(name)
v, err := st.Value(ctx)
if err != nil {
- t.Fatalf("Failed to get mounttable/num-nodes: %v", err)
+ t.Fatalf("Failed to get %q: %v", name, err)
return -1
}
var value int64
if err := vdl.Convert(&value, v); err != nil {
- t.Fatalf("Unexpected value type for mounttable/num-nodes: %v", err)
+ t.Fatalf("Unexpected value type for %q: %v", name, err)
}
return value
}
-func TestNodeCounter(t *testing.T) {
+func nodeCount(t *testing.T, ctx *context.T, addr string) int64 {
+ name := naming.JoinAddressName(addr, "__debug/stats/mounttable/num-nodes")
+ return getCounter(t, ctx, name)
+}
+
+func serverCount(t *testing.T, ctx *context.T, addr string) int64 {
+ name := naming.JoinAddressName(addr, "__debug/stats/mounttable/num-mounted-servers")
+ return getCounter(t, ctx, name)
+}
+
+func TestStatsCounters(t *testing.T) {
rootCtx, shutdown := test.InitForTest()
defer shutdown()
@@ -630,6 +640,9 @@
if expected, got := int64(i+1), nodeCount(t, rootCtx, estr); got != expected {
t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
}
+ if expected, got := int64(i), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of servers. Got %d, expected %d", got, expected)
+ }
}
for i := 1; i <= 10; i++ {
name := fmt.Sprintf("node%d", i)
@@ -641,6 +654,9 @@
if expected, got := int64(11-i), nodeCount(t, rootCtx, estr); got != expected {
t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
}
+ if expected, got := int64(10-i), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of server. Got %d, expected %d", got, expected)
+ }
}
// Test deep tree
@@ -649,14 +665,54 @@
if expected, got := int64(13), nodeCount(t, rootCtx, estr); got != expected {
t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
}
+ if expected, got := int64(2), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of servers. Got %d, expected %d", got, expected)
+ }
doDeleteSubtree(t, rootCtx, estr, "1/2/3/4/5", true)
if expected, got := int64(5), nodeCount(t, rootCtx, estr); got != expected {
t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
}
+ if expected, got := int64(0), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of servers. Got %d, expected %d", got, expected)
+ }
doDeleteSubtree(t, rootCtx, estr, "1", true)
if expected, got := int64(1), nodeCount(t, rootCtx, estr); got != expected {
t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
}
+
+ // Test multiple servers per node
+ for i := 1; i <= 5; i++ {
+ server := naming.JoinAddressName(estr, fmt.Sprintf("addr%d", i))
+ doMount(t, rootCtx, estr, "node1", server, true)
+ doMount(t, rootCtx, estr, "node2", server, true)
+ if expected, got := int64(3), nodeCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
+ }
+ if expected, got := int64(2*i), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of servers. Got %d, expected %d", got, expected)
+ }
+ }
+ doUnmount(t, rootCtx, estr, "node1", "", true)
+ if expected, got := int64(2), nodeCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
+ }
+ if expected, got := int64(5), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of servers. Got %d, expected %d", got, expected)
+ }
+ for i := 1; i <= 5; i++ {
+ server := naming.JoinAddressName(estr, fmt.Sprintf("addr%d", i))
+ doUnmount(t, rootCtx, estr, "node2", server, true)
+ expectedNodes := int64(2)
+ if i == 5 {
+ expectedNodes = 1
+ }
+ if expected, got := expectedNodes, nodeCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of nodes. Got %d, expected %d", got, expected)
+ }
+ if expected, got := int64(5-i), serverCount(t, rootCtx, estr); got != expected {
+ t.Errorf("Unexpected number of servers. Got %d, expected %d", got, expected)
+ }
+ }
}
func initTest() (rootCtx *context.T, aliceCtx *context.T, bobCtx *context.T, shutdown v23.Shutdown) {