Skip to content

Commit

Permalink
Added support for grabbing vbucket detail stats and a vis.
Browse files Browse the repository at this point in the history
  • Loading branch information
dustin committed May 24, 2012
1 parent 5885ba8 commit 2be5a3c
Show file tree
Hide file tree
Showing 3 changed files with 319 additions and 1 deletion.
95 changes: 95 additions & 0 deletions static/vb2.html
@@ -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>
164 changes: 163 additions & 1 deletion static/vbmap.js
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
61 changes: 61 additions & 0 deletions vbmap.go
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net/http"
"strconv"
"strings"

"github.com/couchbaselabs/go-couchbase"
)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.