Increased graph data processing speed (#944)

This commit is contained in:
milux 2020-08-12 21:23:47 +02:00 committed by GitHub
parent c090a398d1
commit 9fe598d774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 78 deletions

View File

@ -6,6 +6,7 @@
"require": { "require": {
"php": "^5.5.9|>=7.0.8", "php": "^5.5.9|>=7.0.8",
"ext-curl": "*", "ext-curl": "*",
"ext-json": "*",
"ext-pdo": "*", "ext-pdo": "*",
"ext-xml": "*", "ext-xml": "*",
"phpmailer/phpmailer": ">=6.0.6 ~6.0", "phpmailer/phpmailer": ">=6.0.6 ~6.0",

View File

@ -29,7 +29,10 @@
namespace psm\Util\Server; namespace psm\Util\Server;
use DateTime;
use psm\Service\Database; use psm\Service\Database;
use Twig\Error\Error;
use Twig_Environment;
/** /**
* History util, create HTML for server graphs * History util, create HTML for server graphs
@ -39,17 +42,17 @@ class HistoryGraph
/** /**
* Database service * Database service
* @var \psm\Service\Database $db; * @var Database $db;
*/ */
protected $db; protected $db;
/** /**
* Twig environment * Twig environment
* @var \Twig_Environment $twig * @var Twig_Environment $twig
*/ */
protected $twig; protected $twig;
public function __construct(Database $db, \Twig_Environment $twig) public function __construct(Database $db, Twig_Environment $twig)
{ {
$this->db = $db; $this->db = $db;
$this->twig = $twig; $this->twig = $twig;
@ -57,7 +60,9 @@ class HistoryGraph
/** /**
* Prepare the HTML for the graph * Prepare the HTML for the graph
* @return string * @param string $server_id ID of server to fetch data for
* @return string Created HTML
* @throws Error On twig error
*/ */
public function createHTML($server_id) public function createHTML($server_id)
{ {
@ -65,9 +70,9 @@ class HistoryGraph
$archive = new ArchiveManager($this->db); $archive = new ArchiveManager($this->db);
$archive->archive($server_id); $archive->archive($server_id);
$now = new \DateTime(); $now = new DateTime();
$last_week = new \DateTime('-1 week 0:0:0'); $last_week = new DateTime('-1 week 0:0:0');
$last_year = new \DateTime('-1 year -1 week 0:0:0'); $last_year = new DateTime('-1 year -1 week 0:0:0');
$graphs = array( $graphs = array(
0 => $this->generateGraphUptime($server_id, $last_week, $now), 0 => $this->generateGraphUptime($server_id, $last_week, $now),
@ -101,8 +106,8 @@ class HistoryGraph
/** /**
* Generate data for uptime graph * Generate data for uptime graph
* @param int $server_id * @param int $server_id
* @param \DateTime $start_time Lowest DateTime of the graph * @param DateTime $start_time Lowest DateTime of the graph
* @param \DateTime $end_time Highest DateTime of the graph * @param DateTime $end_time Highest DateTime of the graph
* @return array * @return array
*/ */
public function generateGraphUptime($server_id, $start_time, $end_time) public function generateGraphUptime($server_id, $start_time, $end_time)
@ -112,9 +117,9 @@ class HistoryGraph
'latency' => array(), 'latency' => array(),
); );
$hour = new \DateTime('-1 hour'); $hour = new DateTime('-1 hour');
$day = new \DateTime('-1 day'); $day = new DateTime('-1 day');
$week = new \DateTime('-1 week'); $week = new DateTime('-1 week');
$records = $this->getRecords('uptime', $server_id, $start_time, $end_time); $records = $this->getRecords('uptime', $server_id, $start_time, $end_time);
@ -148,8 +153,8 @@ class HistoryGraph
/** /**
* Generate data for history graph * Generate data for history graph
* @param int $server_id * @param int $server_id
* @param \DateTime $start_time Lowest DateTime of the graph * @param DateTime $start_time Lowest DateTime of the graph
* @param \DateTime $end_time Highest DateTime of the graph * @param DateTime $end_time Highest DateTime of the graph
* @return array * @return array
*/ */
public function generateGraphHistory($server_id, $start_time, $end_time) public function generateGraphHistory($server_id, $start_time, $end_time)
@ -160,9 +165,9 @@ class HistoryGraph
'latency_max' => array(), 'latency_max' => array(),
); );
$week = new \DateTime('-2 week 0:0:0'); $week = new DateTime('-2 week 0:0:0');
$month = new \DateTime('-1 month -1 week 0:0:0'); $month = new DateTime('-1 month -1 week 0:0:0');
$year = new \DateTime('-1 year -1 week 0:0:0'); $year = new DateTime('-1 year -1 week 0:0:0');
$records = $this->getRecords('history', $server_id, $year, $end_time); $records = $this->getRecords('history', $server_id, $year, $end_time);
@ -197,8 +202,8 @@ class HistoryGraph
* Get all uptime/history records for a server * Get all uptime/history records for a server
* @param string $type * @param string $type
* @param int $server_id * @param int $server_id
* @param \DateTime $start_time Lowest DateTime of the graph * @param DateTime $start_time Lowest DateTime of the graph
* @param \DateTime $end_time Highest DateTime of the graph * @param DateTime $end_time Highest DateTime of the graph
* @return array * @return array
*/ */
protected function getRecords($type, $server_id, $start_time, $end_time) protected function getRecords($type, $server_id, $start_time, $end_time)
@ -207,17 +212,19 @@ class HistoryGraph
return array(); return array();
} }
$records = $this->db->execute( /** @noinspection SqlNoDataSourceInspection */
"SELECT * /** @noinspection SqlResolve */
/** @noinspection PhpUndefinedConstantInspection */
return $this->db->execute(
"SELECT *, UNIX_TIMESTAMP(CONVERT_TZ(`date`, '+00:00', @@session.time_zone)) AS date_ts
FROM `" . PSM_DB_PREFIX . "servers_$type` FROM `" . PSM_DB_PREFIX . "servers_$type`
WHERE `server_id` = :server_id AND `date` BETWEEN :start_time AND :end_time ORDER BY `date` ASC", WHERE `server_id` = :server_id AND `date` BETWEEN :start_time AND :end_time ORDER BY `date`",
array( array(
'server_id' => $server_id, 'server_id' => $server_id,
'start_time' => $start_time->format('Y-m-d H:i:s'), 'start_time' => $start_time->format('Y-m-d H:i:s'),
'end_time' => $end_time->format('Y-m-d H:i:s'), 'end_time' => $end_time->format('Y-m-d H:i:s'),
) )
); );
return $records;
} }
/** /**
@ -225,12 +232,9 @@ class HistoryGraph
* @param array $records All uptime records to parse, MUST BE SORTED BY DATE IN ASCENDING ORDER * @param array $records All uptime records to parse, MUST BE SORTED BY DATE IN ASCENDING ORDER
* @param array $lines Array with keys as line ids to prepare (key must be available in uptime records) * @param array $lines Array with keys as line ids to prepare (key must be available in uptime records)
* @param string $latency_avg_key which key from uptime records to use for calculating averages * @param string $latency_avg_key which key from uptime records to use for calculating averages
* @param \DateTime $start_time Lowest DateTime of the graph * @param DateTime $start_time Lowest DateTime of the graph
* @param \DateTime $end_time Highest DateTime of the graph * @param DateTime $end_time Highest DateTime of the graph
* @param boolean $add_uptime Add uptime calculation? * @param boolean $add_uptime Add uptime calculation?
* @param array $prev Previous result
* @param int $downtime Total downtime
* @param int $prev_downtime Timestamp from last offline record. 0 when last record is uptime
* @return array * @return array
*/ */
protected function generateGraphLines( protected function generateGraphLines(
@ -241,79 +245,90 @@ class HistoryGraph
$end_time, $end_time,
$add_uptime = false $add_uptime = false
) { ) {
$now = new \DateTime(); $now = new DateTime();
$data = array(); $data = array();
// PLEASE NOTE: all times are in microseconds! because of javascript. // PLEASE NOTE: all times are in microseconds! because of javascript.
$latency_avg = 0; $latency_avg = 0;
/** @var array $prev Previous record */
$prev = reset($records); $prev = reset($records);
// Timestamp from last offline record. 0 when last record is up.
$prev_downtime = 0; $prev_downtime = 0;
// Total downtime
$downtime = 0; $downtime = 0;
// The keys of the lines iterated
$line_keys = array_keys($lines);
// Determine whether to process data for the short history graph
$is_short_graph = count($line_keys) === 1 && $line_keys[0] === 'latency';
// get highest latency record for offline height // get highest latency record for offline height
$n = count($records); $highest_latency = 0.0;
$highest_latency = 0.0000; if ($is_short_graph) {
for ($i = 0; $i < $n; $i++) { foreach ($records as $record) {
if (!array_key_exists('latency', $records[$i])) { $latency = (float) $record['latency'];
continue; if ($latency > $highest_latency) {
$highest_latency = $latency;
}
} }
// Update if latency is higher // to ms
$highest_latency = $highest_latency < floatval($records[$i]['latency']) ? $highest_latency = round($highest_latency * 1000);
floatval($records[$i]['latency']) : $highest_latency;
} }
// to ms
$highest_latency = round($highest_latency * 1000);
// Create the list of points and server down zones // Create the list of points and server down zones
foreach ($records as $record) { foreach ($records as $record) {
$time = strtotime($record['date']);
// use the first line to calculate average latency // use the first line to calculate average latency
$latency_avg += (float) $record[$latency_avg_key]; $latency_avg += (float) $record[$latency_avg_key];
foreach ($lines as $key => $value) { if ($is_short_graph) {
// add the value for each of the different lines $time = (int) $record['date_ts'];
if (isset($record[$key])) { // Timestamp in milliseconds
if (isset($record['status'])) { $time_ms = $time * 1000;
// down if (!$record['status']) {
if ($record['status'] == 0) { // down
$lines['online'][] = $prev['status'] $lines['online'][] = $prev['status']
// Previous datapoint was online // Previous datapoint was online
? '{ x: ' . ($time * 1000) . ', y: ' . $prev['latency'] * 1000 . '}' ? ['x' => $time_ms, 'y' => round($prev['latency'] * 1000, 3)]
// Previous datapoint was offline // Previous datapoint was offline
: '{ x: ' . ($time * 1000) . ', y: null}'; : ['x' => $time_ms, 'y' => null];
// new outage start // new outage start
$lines['offline'][] = '{ x: ' . ($time * 1000) . ', y:' . $highest_latency . '}'; $lines['offline'][] = ['x' => $time_ms, 'y' => $highest_latency];
$prev_downtime != 0 ?: $prev_downtime = $time; if ($prev_downtime === 0) {
} else { $prev_downtime = $time;
// up
// outage ends
$lines['offline'][] = $prev['status']
// Previous datapoint was online
? '{ x: ' . ($time * 1000) . ', y:null}'
// Previous datapoint was offline
: '{ x: ' . ($time * 1000) . ', y:' . $highest_latency . '}';
$lines['online'][] = '{ x: ' . ($time * 1000) . ', y: ' .
round((float) $record[$key] * 1000, 2) . '}';
$prev_downtime == 0 ?: $downtime += ($time - $prev_downtime);
$prev_downtime = 0;
}
} else {
$lines[$key][] = '{ x: \'' . $record['date'] . '\', y: ' . $record[$key] * 1000 . '}';
} }
$prev = $record; } else {
// up
// outage ends
$lines['offline'][] = $prev['status']
// Previous datapoint was online
? ['x' => $time_ms, 'y' => null]
// Previous datapoint was offline
: ['x' => $time_ms, 'y' => $highest_latency];
$lines['online'][] = ['x' => $time_ms, 'y' => round($record['latency'] * 1000, 3)];
if ($prev_downtime !== 0) {
$downtime += ($time - $prev_downtime);
}
$prev_downtime = 0;
}
} else {
foreach ($line_keys as $key) {
// add the value for each of the different lines
$lines[$key][] = ['x' => $record['date'], 'y' => $record[$key] * 1000];
} }
} }
$prev = $record;
} }
// Was down before. // Was down before.
// Record the first and last date as a string in the down array // Record the first and last date as a string in the down array
$prev_downtime == 0 ?: $downtime += ($now->getTimestamp() - $prev_downtime); $prev_downtime == 0 ?: $downtime += ($now->getTimestamp() - $prev_downtime);
if ($add_uptime) { if ($add_uptime) {
$prev['status'] ?: $lines['offline'][] = if (!$prev['status']) {
'{ x: ' . ($now->getTimestamp() * 1000) . ', y:' . $highest_latency . '}'; $lines['offline'][] = ['x' => $now->getTimestamp() * 1000, 'y' => $highest_latency];
}
$data['uptime'] = 100 - ($downtime / ($end_time->getTimestamp() - $start_time->getTimestamp())); $data['uptime'] = 100 - ($downtime / ($end_time->getTimestamp() - $start_time->getTimestamp()));
} }
@ -322,11 +337,12 @@ class HistoryGraph
if (empty($line_value)) { if (empty($line_value)) {
continue; continue;
} }
$lines_merged[$line_key]['value'] = implode(', ', $line_value); $lines_merged[$line_key]['value'] = json_encode($line_value);
$lines_merged[$line_key]['name'] = psm_get_lang('servers', $line_key); $lines_merged[$line_key]['name'] = psm_get_lang('servers', $line_key);
} }
$data['latency_avg'] = count($records) > 0 ? ($latency_avg / count($records)) : 0; $n_records = count($records);
$data['latency_avg'] = $n_records > 0 ? ($latency_avg / $n_records) : 0;
$data['lines'] = sizeof($lines_merged) ? $lines_merged : ''; $data['lines'] = sizeof($lines_merged) ? $lines_merged : '';
$data['end_timestamp'] = number_format($end_time->getTimestamp(), 0, '', '') * 1000; $data['end_timestamp'] = number_format($end_time->getTimestamp(), 0, '', '') * 1000;
$data['start_timestamp'] = number_format($start_time->getTimestamp(), 0, '', '') * 1000; $data['start_timestamp'] = number_format($start_time->getTimestamp(), 0, '', '') * 1000;

View File

@ -32,7 +32,7 @@
data: { data: {
datasets: [ datasets: [
{ {
data: [{{ graph.lines.offline.value }}], data: {{ graph.lines.offline.value|raw }},
label: '{{ graph.lines.offline.name }}', label: '{{ graph.lines.offline.name }}',
backgroundColor: '#dc3545', backgroundColor: '#dc3545',
borderColor: '#dc3545', borderColor: '#dc3545',
@ -43,7 +43,7 @@
spanGaps: false, spanGaps: false,
}, },
{ {
data: [{{ graph.lines.online.value }}], data: {{ graph.lines.online.value|raw }},
label: '{{graph.lines.online.name }}', label: '{{graph.lines.online.name }}',
fill: false, fill: false,
spanGaps: false, spanGaps: false,
@ -120,7 +120,7 @@
datasets: [ datasets: [
{% for key,line in graph.lines %} {% for key,line in graph.lines %}
{ {
data: [{{ line.value|raw }}], data: {{ line.value|raw }},
label: '{{ line.name }}', label: '{{ line.name }}',
backgroundColor: colors['{{key}}'], backgroundColor: colors['{{key}}'],
borderColor: colors['{{key}}'], borderColor: colors['{{key}}'],