mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
this structure should allow us to keep the consistent hash code and just use consistent hashing on a subset of nodes, then in order to satisfy the oracle service stuff in functions-service we can just implement a different "Grouper" that does vm allocation and whatever other magic we need to manage nodes and poop out sets of nodes based on tenant id / func. for the suga... see main.go and proxy.go, the rest is basically renaming / moving stuff (not easy to follow changes, nature of the beast). the only 'issues' i can think of is that down in the ch stuff (or Router) we will need a back channel to tell the 'Grouper' to add a node (i.e. all nodes for that shard are currently loaded) which isn't great and also the grouper has no way of knowing that a node in the given set may not be being used anymore. still thinking about how to couple those two. basically don't want to have to just copy that consistent hash code but after munging with stuff i'm almost at 'fuck it' level and maybe it's worth it to just copy and hack it up in functions-service for what we need. we'll also need to have different key funcs for groupers and routers eventually (grouper wants tenant id, router needs tenant id + router). anyway, open to any ideas, i haven't come up with anything great. feedback on interface would be great after this can plumb the datastore stuff into the allGrouper pretty easily
181 lines
4.1 KiB
Go
181 lines
4.1 KiB
Go
package lb
|
|
|
|
const dashPage = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<title>lb dash</title>
|
|
|
|
<script type="text/javascript" src="https://code.jquery.com/jquery-1.10.1.js"></script>
|
|
<script type="text/javascript" src="https://code.highcharts.com/stock/highstock.js"></script>
|
|
<script type="text/javascript" src="https://code.highcharts.com/stock/modules/exporting.js"></script>
|
|
<script>
|
|
var example = 'dynamic-update',
|
|
theme = 'default';
|
|
|
|
var chart; // global
|
|
var seriesMapper = {}; // map by node+func name to chart[i] index
|
|
|
|
Highcharts.setOptions({
|
|
global: {
|
|
useUTC: false
|
|
}
|
|
});
|
|
|
|
function requestData() {
|
|
$.ajax({
|
|
url: '/1/lb/stats',
|
|
success: function(point) {
|
|
var jason = JSON.parse(point);
|
|
|
|
if (!jason["stats"] || jason["stats"].length == 0) {
|
|
// XXX (reed): using server timestamps for real data this can drift easily
|
|
// XXX (reed): uh how to insert empty data point w/o node data? enum all series names?
|
|
//series.addPoint([(new Date()).getTime(), 0], true, shift);
|
|
//series: [{
|
|
//name: 'Random data',
|
|
//data: []
|
|
//}]
|
|
setTimeout(requestData, 1000);
|
|
return
|
|
}
|
|
|
|
for (var i = 0; i < jason["stats"].length; i++) {
|
|
stat = jason["stats"][i];
|
|
var node = stat["node"];
|
|
var func = stat["func"];
|
|
var key = node + func
|
|
|
|
if (seriesMapper[key] == null) {
|
|
chart.addSeries({name: key, data: []});
|
|
waitChart.addSeries({name: key, data: []});
|
|
seriesMapper[key] = chart.series.length - 1;
|
|
}
|
|
|
|
series = chart.series[seriesMapper[key]];
|
|
waitSeries = waitChart.series[seriesMapper[key]];
|
|
|
|
// XXX (reed): hack
|
|
shift = series.data.length > 20 && i == jason["stats"].length + 1;
|
|
|
|
timestamp = Date.parse(stat["timestamp"]);
|
|
console.log(series.data.length, timestamp, stat["tp"], stat["wait"]);
|
|
series.addPoint([timestamp, stat["tp"]], false, shift);
|
|
waitSeries.addPoint([timestamp, stat["wait"]], false, shift);
|
|
}
|
|
if (jason["stats"].length > 0) {
|
|
chart.redraw();
|
|
waitChart.redraw();
|
|
}
|
|
|
|
// call it again after one second
|
|
// XXX (reed): this won't work perfectly cuz if the endpoint fails then we won't ask for more datas
|
|
setTimeout(requestData, 1000);
|
|
},
|
|
cache: false
|
|
});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
chart = new Highcharts.Chart({
|
|
chart: {
|
|
renderTo: 'throughput_chart',
|
|
events: {
|
|
load: requestData
|
|
}
|
|
},
|
|
rangeSelector: {
|
|
buttons: [{
|
|
count: 1,
|
|
type: 'minute',
|
|
text: '1M'
|
|
}, {
|
|
count: 5,
|
|
type: 'minute',
|
|
text: '5M'
|
|
}, {
|
|
type: 'all',
|
|
text: 'All'
|
|
}],
|
|
//inputEnabled: false,
|
|
selected: 0
|
|
},
|
|
title: {
|
|
text: 'lb data'
|
|
},
|
|
exporting: {
|
|
enabled: false
|
|
},
|
|
xAxis: {
|
|
type: 'datetime',
|
|
tickPixelInterval: 150,
|
|
maxZoom: 20 * 1000
|
|
},
|
|
yAxis: {
|
|
minPadding: 0.2,
|
|
maxPadding: 0.2,
|
|
title: {
|
|
text: 'throughput (/s)',
|
|
margin: 80
|
|
}
|
|
},
|
|
series: []
|
|
});
|
|
|
|
waitChart = new Highcharts.Chart({
|
|
chart: {
|
|
renderTo: 'wait_chart',
|
|
events: {
|
|
load: requestData
|
|
}
|
|
},
|
|
rangeSelector: {
|
|
buttons: [{
|
|
count: 1,
|
|
type: 'minute',
|
|
text: '1M'
|
|
}, {
|
|
count: 5,
|
|
type: 'minute',
|
|
text: '5M'
|
|
}, {
|
|
type: 'all',
|
|
text: 'All'
|
|
}],
|
|
//inputEnabled: false,
|
|
selected: 0
|
|
},
|
|
title: {
|
|
text: 'lb data'
|
|
},
|
|
exporting: {
|
|
enabled: false
|
|
},
|
|
xAxis: {
|
|
type: 'datetime',
|
|
tickPixelInterval: 150,
|
|
maxZoom: 20 * 1000
|
|
},
|
|
yAxis: {
|
|
minPadding: 0.2,
|
|
maxPadding: 0.2,
|
|
title: {
|
|
text: 'wait time (seconds)',
|
|
margin: 80
|
|
}
|
|
},
|
|
series: []
|
|
});
|
|
});
|
|
</script>
|
|
|
|
</head>
|
|
<body>
|
|
|
|
<div id="throughput_chart" style="height: 400px; min-width: 310px"></div>
|
|
<div id="wait_chart" style="height: 400px; min-width: 310px"></div>
|
|
|
|
</body>
|
|
</html>
|
|
`
|