Permalink
Browse files

Added support for grabbing vbucket detail stats and a vis.

  • Loading branch information...
1 parent 5885ba8 commit 2be5a3caef2b750b10a5de79f7495fef1b4def09 @dustin dustin committed May 24, 2012
Showing with 319 additions and 1 deletion.
  1. +95 −0 static/vb2.html
  2. +163 −1 static/vbmap.js
  3. +61 −0 vbmap.go
View
@@ -0,0 +1,95 @@
+<html>
+ <head>
+ <title>Cluster VBucket Map</title>
+ <meta http-equiv="refresh" content="21600">
+ <script type="text/javascript" src="/static/jquery.min.js"></script>
+ <script type="text/javascript" src="/static/d3.v2.min.js"></script>
+ <script type="text/javascript" src="/static/vbmap.js"></script>
+ <style type="text/css">
+body {
+ font-family: verdana;
+ background: white;
+ color: black;
+ padding: 0;
+ margin: 0;
+}
+
+#levels {
+ margin: 5px;
+}
+
+#levels rect {
+ stroke: white;
+}
+
+#legend .status {
+ float: right;
+ font-size: smaller;
+ color: #ccc;
+}
+
+#legend .status p {
+ margin: 0;
+}
+
+#nodes {
+ margin: 0;
+ padding: 0;
+ text-indent: 0;
+}
+ </style>
+ </head>
+ <body>
+ <div id="center">
+ <div id="loading">Loading...</div>
+ <div id="levels"></div>
+ </div>
+ <div id="legend">
+ <div class="status">
+ <p>Total items: <span id="total">many</span></p>
+ <p>Largest vbucket (<span id="largestv"></span>): <span id="largestn">many</span></p>
+ <p>Smallest vbucket
+ (<span id="smallestv"></span>): <span id="smallestn">many</span></p>
+ </div>
+ <h2>Nodes</h2>
+ <ul id="nodes"></ul>
+ </div>
+<script type="text/javascript">
+
+ $(document).ready(function() {
+ var clusterInfo = getClusterParams();
+ $("#clusterid").val(clusterInfo.cluster);
+ $("#bucketid").val(clusterInfo.bucket || 'default');
+
+ var chart = makeVBStatThing(window.innerWidth - 50, 600, '#levels');
+
+ function updateGraphs(json) {
+ chart(json);
+ var legend = d3.select("#nodes").selectAll("li").data(json.server_list);
+ legend.enter().append("li");
+
+ legend.text(String)
+ .style("color", function(d) { return chart.color(d); });
+
+ legend.exit().remove();
+
+ $("#total").text(chart.total);
+ $("#largestv").text(chart.largeV);
+ $("#largestn").text(chart.largeN);
+ $("#smallestn").text(chart.smallN);
+ $("#smallestv").text(chart.smallV);
+
+ $("#loading").hide();
+ }
+
+ doVBStatRequest(clusterInfo, updateGraphs, function() {
+ $("#loading").text("Problems initializing data, will keep retrying...");
+ }, function() { $("#clusterform").show(); });
+
+ setInterval(function() {
+ doVBStatRequest(clusterInfo, updateGraphs);
+ }, 5000);
+ });
+ </script>
+ </body>
+</html>
View
@@ -10,6 +10,7 @@ if(!Object.keys) Object.keys = function(o) {
// Base URL for all map requests. Can allow for remote requests.
var mapRequestBase = "/map";
+var statRequestBase = "/vb";
function getClusterParams() {
var s = document.location.search.substring(1);
@@ -122,6 +123,27 @@ function doMapRequest(clusterInfo, fun, errfun, finfun) {
}
}
+function doVBStatRequest(clusterInfo, fun, errfun, finfun) {
+ var params="rand=" + Math.random();
+ if (clusterInfo.cluster) {
+ params += '&cluster=' + clusterInfo.cluster;
+ }
+ if (clusterInfo.bucket) {
+ params += '&bucket=' + clusterInfo.bucket;
+ }
+ console.log("Requesting", statRequestBase + "?" + params);
+ d3.json(statRequestBase + "?" + params, function(json) {
+ if (json != null) {
+ fun(json);
+ } else if(errfun) {
+ errfun();
+ }
+ if (finfun) {
+ finfun();
+ }
+ });
+}
+
function makeState(w, h, container) {
function countChildren(d) {
@@ -846,4 +868,144 @@ function makeVBThing(w, h, container) {
}
return update;
-}
+}
+
+function makeVBStatThing(totalWidth, h, container) {
+ var chart = d3.select(container + " svg g.canvas");
+ chart = d3.select(container)
+ .append("svg")
+ .attr("width", w)
+ .attr("height", h)
+ .append("g")
+ .attr("transform", "translate(50, 15)");
+
+ var w = 5;
+
+ var maxvalue = 1;
+
+ var x;
+ var y;
+
+ var color = d3.scale.category20();
+
+ chart.append("g").attr("class", "rulex");
+ chart.append("g").attr("class", "ruley");
+ chart.append("g").attr("class", "plot");
+
+ function update(json) {
+ update.color = color;
+ var data = [];
+ var total = 0;
+ update.largeV = 0, update.largeN = 0, update.smallV = 0, update.smallN = 10000000000000;
+ for (var node in json.stats) {
+ var nstates = json.stats[node];
+ for (var vbid in nstates) {
+ var n = parseInt(nstates[vbid].num_items);
+ if (n > update.largeN) {
+ update.largeN = n;
+ update.largeV = vbid;
+ }
+ if (n < update.smallN) {
+ update.smallN = n;
+ update.smallV = vbid;
+ }
+ data[vbid] = n;
+ total += n;
+ maxvalue = Math.max(maxvalue, n);
+ }
+ }
+
+ function master(n) {
+ return json.server_list[json.repmap[n][0]];
+ }
+
+ w = Math.max(2, Math.floor(totalWidth / data.length));
+
+ x = d3.scale.linear()
+ .domain([0, 1])
+ .range([0, w]);
+
+ y = d3.scale.linear()
+ .domain([0, maxvalue])
+ .rangeRound([0, h]);
+
+ chart.select(".plot").selectAll("rect")
+ .data(data)
+ .enter().append("rect")
+ .attr("x", function(d, i) { return x(i) - .5; })
+ .attr("y", function(d) { return h - y(d) - .5; })
+ .attr("width", w)
+ .attr("height", function(d) { return y(d); })
+ .style("fill", function(d, i) { return color(master(i));});
+
+ chart.select(".plot").selectAll("rect")
+ .data(data)
+ .transition()
+ .duration(1000)
+ .style("fill", function(d, i) { return color(master(i));})
+ .attr("y", function(d) { return h - y(d) - .5; })
+ .attr("height", function(d) { return y(d); });
+
+ var ruler = d3.scale.linear()
+ .domain([0, data.length])
+ .range([0, data.length]);
+
+ chart.select(".rulex").selectAll("line")
+ .data(ruler.ticks(10))
+ .enter().append("line")
+ .attr("x1", x)
+ .attr("x2", x)
+ .attr("y1", 0)
+ .attr("y2", h)
+ .style("stroke", "#ccc");
+
+ chart.select(".rulex").selectAll(".rulex")
+ .data(ruler.ticks(10))
+ .enter().append("text")
+ .attr("class", "rulex")
+ .attr("x", x)
+ .attr("y", 0)
+ .attr("dy", -3)
+ .attr("text-anchor", "middle")
+ .text(function(d) { return d > 0 ? d : ""; });
+
+ chart.select(".ruley").selectAll("line")
+ .data(y.ticks(10))
+ .enter().append("line")
+ .attr("x1", 0)
+ .attr("x2", totalWidth)
+ .attr("y1", y)
+ .attr("y2", y)
+ .style("stroke", "#ccc");
+
+ chart.select(".ruley").selectAll("line")
+ .data(y.ticks(10))
+ .attr("y1", y)
+ .attr("y2", y);
+
+ chart.select(".ruley").selectAll("line")
+ .data(y.ticks(10))
+ .exit().remove();
+
+ chart.select(".ruley").selectAll(".rule")
+ .data(y.ticks(10))
+ .enter().append("text")
+ .attr("class", "rule")
+ .attr("x", 0)
+ .attr("dx", -25)
+ .attr("y", y)
+ .attr("text-anchor", "middle")
+ .text(function(d) { return maxvalue - d; });
+
+ chart.select(".ruley").selectAll(".rule")
+ .data(y.ticks(10))
+ .attr("y", y)
+ .text(function(d) { return maxvalue - d; });
+
+ chart.select(".ruley").selectAll(".rule")
+ .data(y.ticks(10))
+ .exit().remove();
+ }
+
+ return update;
+}
View
@@ -8,6 +8,7 @@ import (
"log"
"net/http"
"strconv"
+ "strings"
"github.com/couchbaselabs/go-couchbase"
)
@@ -113,6 +114,65 @@ func mapHandler(w http.ResponseWriter, req *http.Request) {
}
}
+type vbstats map[string]map[string]interface{}
+
+func getVbStats(bucket *couchbase.Bucket, commonSuffixMC string) map[string]vbstats {
+
+ allstats := bucket.GetStats("vbucket-details")
+
+ rv := map[string]vbstats{}
+ for fullname, m := range allstats {
+ sn := couchbase.CleanupHost(fullname, commonSuffixMC)
+ rv[sn] = vbstats{}
+
+ for k, v := range m {
+ var parts = strings.Split(k[3:], ":")
+ vbbig, err := strconv.ParseInt(parts[0], 10, 16)
+ maybefatal(err, "Error parsing vbucket: %v/%v: %v",
+ k, v, err)
+ vb := fmt.Sprintf("%d", vbbig)
+ label := "state"
+ if len(parts) == 2 {
+ label = parts[1]
+ }
+ d, ok := rv[sn][vb]
+ if !ok {
+ d = make(map[string]interface{})
+ rv[sn][vb] = d
+ }
+ rv[sn][vb][label] = v
+ }
+ }
+ return rv
+}
+
+func vbHandler(w http.ResponseWriter, req *http.Request) {
+ w.Header().Set("Content-type", "application/javascript")
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+
+ bucket := getBucket(req)
+ defer bucket.Close()
+
+ commonSuffixMC := couchbase.FindCommonSuffix(bucket.VBucketServerMap.ServerList)
+
+ rv := map[string]interface{}{}
+ rv["server_list"] = getShortServerList(bucket, commonSuffixMC)
+ rv["stats"] = getVbStats(bucket, commonSuffixMC)
+ rv["repmap"] = bucket.VBucketServerMap.VBucketMap
+
+ req.ParseForm()
+ var_name := req.FormValue("name")
+
+ if var_name != "" {
+ fmt.Fprintf(w, "var "+var_name+" = ")
+ }
+ err := json.NewEncoder(w).Encode(rv)
+ maybefatal(err, "Error encoding output: %v", err)
+ if var_name != "" {
+ fmt.Fprintf(w, ";")
+ }
+}
+
type handler func(http.ResponseWriter, *http.Request)
func files(contentType string, paths ...string) handler {
@@ -164,5 +224,6 @@ func main() {
} else {
http.HandleFunc("/map", mapHandler)
}
+ http.HandleFunc("/vb", vbHandler)
log.Fatal(http.ListenAndServe(":4444", nil))
}

0 comments on commit 2be5a3c

Please sign in to comment.