ver updates to new metrics

This commit is contained in:
Lex Berezhny 2019-07-18 19:18:14 -04:00
parent 08da763327
commit 395518ff76
2 changed files with 193 additions and 92 deletions

View file

@ -20,14 +20,11 @@ class ServerConnection {
channel = IOWebSocketChannel.connect(this.url);
int tick = 1;
channel.stream.listen((message) {
Map data = json.decode(message);
var data = json.decode(message);
print(data);
Map commands = data['commands'] ?? {};
_loadDataController.add(
ServerLoadDataPoint(
tick,
APICallMetrics.from_map(commands['search'] ?? {}),
APICallMetrics.from_map(commands['resolve'] ?? {})
ServerLoadDataPoint.from_map(
tick, data
)
);
tick++;
@ -52,48 +49,106 @@ class ServerConnection {
}
class TimeStats {
final int avg, min, max;
// percentiles
final int five;
final int twenty_five;
final int fifty;
final int seventy_five;
final int ninety_five;
TimeStats(
this.avg, this.min, this.five, this.twenty_five,
this.fifty, this.seventy_five, this.ninety_five,
this.max
);
TimeStats.from_list(List l): this(
l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7]
);
TimeStats.from_zeros(): this(
0, 0, 0, 0, 0, 0, 0, 0
);
factory TimeStats.from_list_or_zeros(List l) =>
l != null ? TimeStats.from_list(l): TimeStats.from_zeros();
}
class APICallMetrics {
final int started;
final int finished;
final int total_time;
final int execution_time;
final int query_time;
final int query_count;
final int cache_hit;
final int avg_wait_time;
final int avg_total_time;
final int avg_execution_time;
final int avg_query_time_per_search;
final int avg_query_time_per_query;
// total requests received
final int receive_count;
// sum of these is total responses made
final int cache_response_count;
final int query_response_count;
final int intrp_response_count;
final int error_response_count;
// stacked values for chart
final int cache_response_stack;
final int query_response_stack;
final int intrp_response_stack;
final int error_response_stack;
// millisecond timings for non-cache responses
final TimeStats response;
final TimeStats interrupt;
final TimeStats error;
// response, interrupt and error each also report the python, wait and sql stats:
final TimeStats python;
final TimeStats wait;
final TimeStats sql;
// extended timings for individual sql executions
final TimeStats individual_sql;
final int individual_sql_count;
// actual queries
final List<String> errored_queries;
final List<String> interrupted_queries;
APICallMetrics(
this.started, this.finished, this.total_time, this.execution_time,
this.query_time, this.query_count, this.cache_hit):
avg_wait_time=finished > 0 ? ((total_time - (execution_time + query_time))/finished).round() : 0,
avg_total_time=finished > 0 ? (total_time/finished).round() : 0,
avg_execution_time=finished > 0 ? (execution_time/finished).round() : 0,
avg_query_time_per_search=finished > 0 ? (query_time/finished).round() : 0,
avg_query_time_per_query=query_count > 0 ? (query_time/query_count).round() : 0;
this.receive_count,
this.cache_response_count, this.query_response_count,
this.intrp_response_count, this.error_response_count,
this.response, this.interrupt, this.error,
this.python, this.wait, this.sql,
this.individual_sql, this.individual_sql_count,
this.errored_queries, this.interrupted_queries
):
cache_response_stack=cache_response_count+query_response_count+intrp_response_count+error_response_count,
query_response_stack=query_response_count+intrp_response_count+error_response_count,
intrp_response_stack=intrp_response_count+error_response_count,
error_response_stack=error_response_count;
APICallMetrics.from_map(Map data): this(
data['started'] ?? 0,
data['finished'] ?? 0,
data['total_time'] ?? 0,
data['execution_time'] ?? 0,
data['query_time'] ?? 0,
data['query_count'] ?? 0,
data['cache_hit'] ?? 0,
data["receive_count"] ?? 0,
data["cache_response_count"] ?? 0,
data["query_response_count"] ?? 0,
data["intrp_response_count"] ?? 0,
data["error_response_count"] ?? 0,
TimeStats.from_list_or_zeros(data["response"]),
TimeStats.from_list_or_zeros(data["interrupt"]),
TimeStats.from_list_or_zeros(data["error"]),
TimeStats.from_list_or_zeros(data["python"]),
TimeStats.from_list_or_zeros(data["wait"]),
TimeStats.from_list_or_zeros(data["sql"]),
TimeStats.from_list_or_zeros(data["individual_sql"]),
data["individual_sql_count"] ?? 0,
List<String>.from(data["errored_queries"] ?? const []),
List<String>.from(data["interrupted_queries"] ?? const []),
);
}
class ServerLoadDataPoint {
final int tick;
final int sessions;
final APICallMetrics search;
final APICallMetrics resolve;
ServerLoadDataPoint(this.tick, this.search, this.resolve);
ServerLoadDataPoint.empty():
tick = 0,
search=APICallMetrics.from_map({}),
resolve=APICallMetrics.from_map({});
const ServerLoadDataPoint(
this.tick, this.sessions, this.search, this.resolve
);
ServerLoadDataPoint.from_map(int tick, Map data): this(
tick, (data['status'] ?? const {})['sessions'] ?? 0,
APICallMetrics.from_map((data['api'] ?? const {})['search'] ?? const {}),
APICallMetrics.from_map((data['api'] ?? const {})['resolve'] ?? const {})
);
ServerLoadDataPoint.empty(): this(
0, 0, APICallMetrics.from_map(const {}), APICallMetrics.from_map(const {})
);
}

View file

@ -13,7 +13,12 @@ class ServerCharts extends StatelessWidget {
Widget build(BuildContext context) {
var server = Provider.of<Server>(context, listen: false);
return ListView(children: <Widget>[
SizedBox(height: 300.0, child: ServerLoadChart(server)),
SizedBox(height: 300.0, child: APILoadChart(
server, "Search", (ServerLoadDataPoint dataPoint) => dataPoint.search
)),
//SizedBox(height: 300.0, child: APILoadChart(
// server, "Resolve", (ServerLoadDataPoint dataPoint) => dataPoint.resolve
//)),
SizedBox(height: 300.0, child: ServerPerformanceChart(server)),
//SizedBox(height: 220.0, child: ClientLoadChart(server.clientLoadManager)),
//SizedBox(height: 220.0, child: ClientPerformanceChart(server.clientLoadManager)),
@ -22,16 +27,20 @@ class ServerCharts extends StatelessWidget {
}
class ServerLoadChart extends StatefulWidget {
typedef APICallMetrics APIGetter(ServerLoadDataPoint dataPoint);
class APILoadChart extends StatefulWidget {
final Server server;
ServerLoadChart(this.server);
final String name;
final APIGetter getter;
APILoadChart(this.server, this.name, this.getter);
@override
State<StatefulWidget> createState() => ServerLoadChartState();
State<StatefulWidget> createState() => APILoadChartState();
}
class ServerLoadChartState extends State<ServerLoadChart> {
class APILoadChartState extends State<APILoadChart> {
List<charts.Series<ServerLoadDataPoint, int>> seriesData;
@ -39,54 +48,73 @@ class ServerLoadChartState extends State<ServerLoadChart> {
void initState() {
super.initState();
seriesData = [
/*
charts.Series<ServerLoadDataPoint, int>(
id: 'Search Cache',
id: 'Received',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault.lighter,
strokeWidthPxFn: (_, __) => 4.0,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).receive_count,
data: widget.server.serverLoadData,
),*/
charts.Series<ServerLoadDataPoint, int>(
id: 'Cache',
colorFn: (_, __) =>
charts.MaterialPalette.green.shadeDefault,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).cache_response_stack,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Query',
colorFn: (_, __) =>
charts.MaterialPalette.blue.shadeDefault,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).query_response_stack,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Interrupts',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.lighter,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).intrp_response_stack,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Errors',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).error_response_stack,
data: widget.server.serverLoadData,
),
/*
charts.Series<ServerLoadDataPoint, int>(
id: '${widget.name} Interrupted',
colorFn: (_, __) =>
charts.MaterialPalette.pink.shadeDefault.lighter,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).interrupted,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 5.0,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: '${widget.name} Errored',
colorFn: (_, __) =>
charts.MaterialPalette.red.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).errored,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 5.0,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: '${widget.name} From Cache',
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.cache_hit,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Search Finish',
colorFn: (_, __) =>
charts.MaterialPalette.deepOrange.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.finished,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 5.0,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Search Start',
colorFn: (_, __) =>
charts.MaterialPalette.deepOrange.shadeDefault.lighter,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.started,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 1.0,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Resolve Cache',
colorFn: (_, __) => charts.MaterialPalette.cyan.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.resolve.cache_hit,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Resolve Finish',
colorFn: (_, __) => charts.MaterialPalette.teal.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.resolve.finished,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 5.0,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Resolve Start',
colorFn: (_, __) => charts.MaterialPalette.teal.shadeDefault.lighter,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.resolve.started,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 1.0,
measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).cache_hits,
strokeWidthPxFn: (ServerLoadDataPoint load, _) => 3.0,
data: widget.server.serverLoadData,
),
*/
];
}
@ -94,7 +122,9 @@ class ServerLoadChartState extends State<ServerLoadChart> {
Widget build(BuildContext context) {
return StreamBuilder<ServerLoadDataPoint>(
stream: widget.server.serverLoadStream,
builder: (BuildContext context, _) => BetterLineChart(seriesData)
builder: (BuildContext context, _) => BetterLineChart(seriesData,
//renderer: new charts.LineRendererConfig<num>(includeArea: true)
)
);
}
}
@ -117,27 +147,41 @@ class ServerPerformanceChartState extends State<ServerPerformanceChart> {
void initState() {
super.initState();
seriesData = [
charts.Series<ServerLoadDataPoint, int>(
id: 'Waiting 95 Percentile',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.lighter,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.wait.ninety_five,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Avg. Waiting',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.avg_wait_time,
measureFn: (ServerLoadDataPoint load, _) => load.search.wait.avg,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Avg. Executing',
colorFn: (_, __) => charts.MaterialPalette.teal.shadeDefault.lighter,
colorFn: (_, __) => charts.MaterialPalette.teal.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.avg_execution_time,
measureFn: (ServerLoadDataPoint load, _) => load.search.python.avg,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'SQLite 95 Percentile',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault.lighter,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.sql.ninety_five,
data: widget.server.serverLoadData,
),
charts.Series<ServerLoadDataPoint, int>(
id: 'Avg. SQLite',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.avg_query_time_per_search,
measureFn: (ServerLoadDataPoint load, _) => load.search.sql.avg,
data: widget.server.serverLoadData,
)
),
];
}
@ -260,11 +304,13 @@ class BetterLineChart extends charts.LineChart {
final int itemCount;
final Object lastItem;
BetterLineChart(List<charts.Series<dynamic, int>> seriesList):
BetterLineChart(List<charts.Series<dynamic, int>> seriesList, {charts.LineRendererConfig renderer}):
itemCount = seriesList[0].data.length,
lastItem = seriesList[0].data.last,
super(
seriesList,
animate: false,
defaultRenderer: renderer,
behaviors: [charts.SeriesLegend()],
domainAxis: charts.NumericAxisSpec(
viewport: new charts.NumericExtents(