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); channel = IOWebSocketChannel.connect(this.url);
int tick = 1; int tick = 1;
channel.stream.listen((message) { channel.stream.listen((message) {
Map data = json.decode(message); var data = json.decode(message);
print(data); print(data);
Map commands = data['commands'] ?? {};
_loadDataController.add( _loadDataController.add(
ServerLoadDataPoint( ServerLoadDataPoint.from_map(
tick, tick, data
APICallMetrics.from_map(commands['search'] ?? {}),
APICallMetrics.from_map(commands['resolve'] ?? {})
) )
); );
tick++; 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 { class APICallMetrics {
final int started; // total requests received
final int finished; final int receive_count;
final int total_time; // sum of these is total responses made
final int execution_time; final int cache_response_count;
final int query_time; final int query_response_count;
final int query_count; final int intrp_response_count;
final int cache_hit; final int error_response_count;
final int avg_wait_time; // stacked values for chart
final int avg_total_time; final int cache_response_stack;
final int avg_execution_time; final int query_response_stack;
final int avg_query_time_per_search; final int intrp_response_stack;
final int avg_query_time_per_query; 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( APICallMetrics(
this.started, this.finished, this.total_time, this.execution_time, this.receive_count,
this.query_time, this.query_count, this.cache_hit): this.cache_response_count, this.query_response_count,
avg_wait_time=finished > 0 ? ((total_time - (execution_time + query_time))/finished).round() : 0, this.intrp_response_count, this.error_response_count,
avg_total_time=finished > 0 ? (total_time/finished).round() : 0, this.response, this.interrupt, this.error,
avg_execution_time=finished > 0 ? (execution_time/finished).round() : 0, this.python, this.wait, this.sql,
avg_query_time_per_search=finished > 0 ? (query_time/finished).round() : 0, this.individual_sql, this.individual_sql_count,
avg_query_time_per_query=query_count > 0 ? (query_time/query_count).round() : 0; 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( APICallMetrics.from_map(Map data): this(
data['started'] ?? 0, data["receive_count"] ?? 0,
data['finished'] ?? 0, data["cache_response_count"] ?? 0,
data['total_time'] ?? 0, data["query_response_count"] ?? 0,
data['execution_time'] ?? 0, data["intrp_response_count"] ?? 0,
data['query_time'] ?? 0, data["error_response_count"] ?? 0,
data['query_count'] ?? 0, TimeStats.from_list_or_zeros(data["response"]),
data['cache_hit'] ?? 0, 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 { class ServerLoadDataPoint {
final int tick; final int tick;
final int sessions;
final APICallMetrics search; final APICallMetrics search;
final APICallMetrics resolve; final APICallMetrics resolve;
ServerLoadDataPoint(this.tick, this.search, this.resolve); const ServerLoadDataPoint(
ServerLoadDataPoint.empty(): this.tick, this.sessions, this.search, this.resolve
tick = 0, );
search=APICallMetrics.from_map({}), ServerLoadDataPoint.from_map(int tick, Map data): this(
resolve=APICallMetrics.from_map({}); 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) { Widget build(BuildContext context) {
var server = Provider.of<Server>(context, listen: false); var server = Provider.of<Server>(context, listen: false);
return ListView(children: <Widget>[ 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: 300.0, child: ServerPerformanceChart(server)),
//SizedBox(height: 220.0, child: ClientLoadChart(server.clientLoadManager)), //SizedBox(height: 220.0, child: ClientLoadChart(server.clientLoadManager)),
//SizedBox(height: 220.0, child: ClientPerformanceChart(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; final Server server;
ServerLoadChart(this.server); final String name;
final APIGetter getter;
APILoadChart(this.server, this.name, this.getter);
@override @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; List<charts.Series<ServerLoadDataPoint, int>> seriesData;
@ -39,54 +48,73 @@ class ServerLoadChartState extends State<ServerLoadChart> {
void initState() { void initState() {
super.initState(); super.initState();
seriesData = [ seriesData = [
/*
charts.Series<ServerLoadDataPoint, int>( 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, colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick, domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.cache_hit, measureFn: (ServerLoadDataPoint load, _) => widget.getter(load).cache_hits,
data: widget.server.serverLoadData, strokeWidthPxFn: (ServerLoadDataPoint load, _) => 3.0,
),
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,
data: widget.server.serverLoadData, data: widget.server.serverLoadData,
), ),
*/
]; ];
} }
@ -94,7 +122,9 @@ class ServerLoadChartState extends State<ServerLoadChart> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<ServerLoadDataPoint>( return StreamBuilder<ServerLoadDataPoint>(
stream: widget.server.serverLoadStream, 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() { void initState() {
super.initState(); super.initState();
seriesData = [ 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>( charts.Series<ServerLoadDataPoint, int>(
id: 'Avg. Waiting', id: 'Avg. Waiting',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.darker, colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick, domainFn: (ServerLoadDataPoint load, _) => load.tick,
measureFn: (ServerLoadDataPoint load, _) => load.search.avg_wait_time, measureFn: (ServerLoadDataPoint load, _) => load.search.wait.avg,
data: widget.server.serverLoadData, data: widget.server.serverLoadData,
), ),
charts.Series<ServerLoadDataPoint, int>( charts.Series<ServerLoadDataPoint, int>(
id: 'Avg. Executing', id: 'Avg. Executing',
colorFn: (_, __) => charts.MaterialPalette.teal.shadeDefault.lighter, colorFn: (_, __) => charts.MaterialPalette.teal.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick, 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, data: widget.server.serverLoadData,
), ),
charts.Series<ServerLoadDataPoint, int>( charts.Series<ServerLoadDataPoint, int>(
id: 'Avg. SQLite', id: 'Avg. SQLite',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault.darker, colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault.darker,
domainFn: (ServerLoadDataPoint load, _) => load.tick, 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, data: widget.server.serverLoadData,
) ),
]; ];
} }
@ -260,11 +304,13 @@ class BetterLineChart extends charts.LineChart {
final int itemCount; final int itemCount;
final Object lastItem; 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, itemCount = seriesList[0].data.length,
lastItem = seriesList[0].data.last, lastItem = seriesList[0].data.last,
super( super(
seriesList, seriesList,
animate: false,
defaultRenderer: renderer,
behaviors: [charts.SeriesLegend()], behaviors: [charts.SeriesLegend()],
domainAxis: charts.NumericAxisSpec( domainAxis: charts.NumericAxisSpec(
viewport: new charts.NumericExtents( viewport: new charts.NumericExtents(