mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Stop storing a node-manager wide ringbuffer of data utilization, instead request it when it's wanted. Use the same MultiRingBuffer class, with added render method, to handle front-page utilization.
This commit is contained in:
parent
39f605e195
commit
fc5b9ad3d4
@ -46,14 +46,12 @@ fn rocket() -> _ {
|
||||
static_pages::klingon,
|
||||
// API calls
|
||||
tracker::current_throughput,
|
||||
tracker::throughput_ring,
|
||||
tracker::cpu_usage,
|
||||
tracker::ram_usage,
|
||||
tracker::top_10_downloaders,
|
||||
tracker::worst_10_rtt,
|
||||
tracker::rtt_histogram,
|
||||
tracker::host_counts,
|
||||
tracker::busy_quantile,
|
||||
shaped_devices::all_shaped_devices,
|
||||
shaped_devices::shaped_devices_count,
|
||||
shaped_devices::shaped_devices_range,
|
||||
|
@ -5,9 +5,7 @@
|
||||
mod cpu_ram;
|
||||
mod lqosd_stats;
|
||||
mod shaped_devices;
|
||||
mod throughput;
|
||||
|
||||
pub use cpu_ram::*;
|
||||
pub use lqosd_stats::*;
|
||||
pub use shaped_devices::*;
|
||||
pub use throughput::*;
|
||||
|
@ -1,63 +0,0 @@
|
||||
use lazy_static::*;
|
||||
use parking_lot::RwLock;
|
||||
use rocket::serde::Serialize;
|
||||
|
||||
lazy_static! {
|
||||
/// Global storage of the current throughput counter.
|
||||
pub static ref CURRENT_THROUGHPUT : RwLock<ThroughputPerSecond> = RwLock::new(ThroughputPerSecond::default());
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Global storage of the last N seconds throughput buffer.
|
||||
pub static ref THROUGHPUT_BUFFER : RwLock<ThroughputRingbuffer> = RwLock::new(ThroughputRingbuffer::new());
|
||||
}
|
||||
|
||||
/// Stores total system throughput per second.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Default)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct ThroughputPerSecond {
|
||||
pub bits_per_second: (u64, u64),
|
||||
pub packets_per_second: (u64, u64),
|
||||
pub shaped_bits_per_second: (u64, u64),
|
||||
}
|
||||
|
||||
/// How many entries (at one per second) should we keep in the
|
||||
/// throughput ringbuffer?
|
||||
const RINGBUFFER_SAMPLES: usize = 300;
|
||||
|
||||
/// Stores Throughput samples in a ringbuffer, continually
|
||||
/// updating. There are always RINGBUFFER_SAMPLES available,
|
||||
/// allowing for non-allocating/non-growing storage of
|
||||
/// throughput for the dashboard summaries.
|
||||
pub struct ThroughputRingbuffer {
|
||||
readings: Vec<ThroughputPerSecond>,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl ThroughputRingbuffer {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
readings: vec![ThroughputPerSecond::default(); RINGBUFFER_SAMPLES],
|
||||
next: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store(&mut self, reading: ThroughputPerSecond) {
|
||||
self.readings[self.next] = reading;
|
||||
self.next += 1;
|
||||
self.next %= RINGBUFFER_SAMPLES;
|
||||
}
|
||||
|
||||
pub fn get_result(&self) -> Vec<ThroughputPerSecond> {
|
||||
let mut result = Vec::with_capacity(RINGBUFFER_SAMPLES);
|
||||
|
||||
for i in self.next..RINGBUFFER_SAMPLES {
|
||||
result.push(self.readings[i]);
|
||||
}
|
||||
for i in 0..self.next {
|
||||
result.push(self.readings[i]);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
@ -129,7 +129,6 @@ fn watch_for_shaped_devices_changing() -> Result<()> {
|
||||
async fn get_data_from_server() -> Result<()> {
|
||||
// Send request to lqosd
|
||||
let requests = vec![
|
||||
BusRequest::GetCurrentThroughput,
|
||||
BusRequest::GetTopNDownloaders { start: 0, end: 10 },
|
||||
BusRequest::GetWorstRtt { start: 0, end: 10 },
|
||||
BusRequest::RttHistogram,
|
||||
@ -137,26 +136,7 @@ async fn get_data_from_server() -> Result<()> {
|
||||
];
|
||||
|
||||
for r in bus_request(requests).await?.iter() {
|
||||
match r {
|
||||
BusResponse::CurrentThroughput {
|
||||
bits_per_second,
|
||||
packets_per_second,
|
||||
shaped_bits_per_second,
|
||||
} => {
|
||||
{
|
||||
let mut lock = CURRENT_THROUGHPUT.write();
|
||||
lock.bits_per_second = *bits_per_second;
|
||||
lock.packets_per_second = *packets_per_second;
|
||||
} // Lock scope
|
||||
{
|
||||
let mut lock = THROUGHPUT_BUFFER.write();
|
||||
lock.store(ThroughputPerSecond {
|
||||
packets_per_second: *packets_per_second,
|
||||
bits_per_second: *bits_per_second,
|
||||
shaped_bits_per_second: *shaped_bits_per_second,
|
||||
});
|
||||
}
|
||||
}
|
||||
match r {
|
||||
BusResponse::TopDownloaders(stats) => {
|
||||
*TOP_10_DOWNLOADERS.write() = stats.clone();
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
mod cache;
|
||||
mod cache_manager;
|
||||
use self::cache::{
|
||||
CPU_USAGE, CURRENT_THROUGHPUT, HOST_COUNTS, NUM_CPUS, RAM_USED,
|
||||
RTT_HISTOGRAM, THROUGHPUT_BUFFER, TOP_10_DOWNLOADERS, TOTAL_RAM,
|
||||
CPU_USAGE, HOST_COUNTS, NUM_CPUS, RAM_USED,
|
||||
RTT_HISTOGRAM, TOP_10_DOWNLOADERS, TOTAL_RAM,
|
||||
WORST_10_RTT,
|
||||
};
|
||||
use crate::{auth_guard::AuthGuard, tracker::cache::ThroughputPerSecond};
|
||||
use crate::auth_guard::AuthGuard;
|
||||
pub use cache::{SHAPED_DEVICES, UNKNOWN_DEVICES};
|
||||
pub use cache_manager::update_tracking;
|
||||
use lazy_static::lazy_static;
|
||||
use lqos_bus::{IpStats, TcHandle};
|
||||
use lqos_bus::{IpStats, TcHandle, bus_request, BusRequest, BusResponse};
|
||||
use lqos_config::LibreQoSConfig;
|
||||
use parking_lot::Mutex;
|
||||
use rocket::serde::{json::Json, Deserialize, Serialize};
|
||||
@ -60,15 +60,31 @@ impl From<&IpStats> for IpStatsWithPlan {
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/current_throughput")]
|
||||
pub fn current_throughput(_auth: AuthGuard) -> Json<ThroughputPerSecond> {
|
||||
let result = *CURRENT_THROUGHPUT.read();
|
||||
Json(result)
|
||||
/// Stores total system throughput per second.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Default)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct ThroughputPerSecond {
|
||||
pub bits_per_second: (u64, u64),
|
||||
pub packets_per_second: (u64, u64),
|
||||
pub shaped_bits_per_second: (u64, u64),
|
||||
}
|
||||
|
||||
#[get("/api/throughput_ring")]
|
||||
pub fn throughput_ring(_auth: AuthGuard) -> Json<Vec<ThroughputPerSecond>> {
|
||||
let result = THROUGHPUT_BUFFER.read().get_result();
|
||||
#[get("/api/current_throughput")]
|
||||
pub async fn current_throughput(_auth: AuthGuard) -> Json<ThroughputPerSecond> {
|
||||
let mut result = ThroughputPerSecond::default();
|
||||
if let Ok(messages) = bus_request(vec![BusRequest::GetCurrentThroughput]).await {
|
||||
for msg in messages {
|
||||
if let BusResponse::CurrentThroughput {
|
||||
bits_per_second,
|
||||
packets_per_second,
|
||||
shaped_bits_per_second,
|
||||
} = msg {
|
||||
result.bits_per_second = bits_per_second;
|
||||
result.packets_per_second = packets_per_second;
|
||||
result.shaped_bits_per_second = shaped_bits_per_second;
|
||||
}
|
||||
}
|
||||
}
|
||||
Json(result)
|
||||
}
|
||||
|
||||
@ -123,28 +139,3 @@ lazy_static! {
|
||||
Mutex::new(lqos_config::LibreQoSConfig::load().unwrap());
|
||||
}
|
||||
|
||||
#[get("/api/busy_quantile")]
|
||||
pub fn busy_quantile(_auth: AuthGuard) -> Json<Vec<(u32, u32)>> {
|
||||
let (down_capacity, up_capacity) = {
|
||||
let lock = CONFIG.lock();
|
||||
(
|
||||
lock.total_download_mbps as f64 * 1_000_000.0,
|
||||
lock.total_upload_mbps as f64 * 1_000_000.0,
|
||||
)
|
||||
};
|
||||
let throughput = THROUGHPUT_BUFFER.read().get_result();
|
||||
let mut result = vec![(0, 0); 10];
|
||||
throughput.iter().for_each(|tp| {
|
||||
let (down, up) = tp.bits_per_second;
|
||||
let (down, up) = (down * 8, up * 8);
|
||||
//println!("{down_capacity}, {up_capacity}, {down}, {up}");
|
||||
let (down, up) = (
|
||||
if down_capacity > 0.0 { down as f64 / down_capacity } else { 0.0 },
|
||||
if up_capacity > 0.0 { up as f64 / up_capacity } else { 0.0 },
|
||||
);
|
||||
let (down, up) = ((down * 10.0) as usize, (up * 10.0) as usize);
|
||||
result[usize::min(9, down)].0 += 1;
|
||||
result[usize::min(0, up)].1 += 1;
|
||||
});
|
||||
Json(result)
|
||||
}
|
||||
|
@ -229,11 +229,7 @@ class MultiRingBuffer {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*let v = buffers[rootName];
|
||||
let dn = { x: v.x_axis, y: v.download, name: "DL", type: 'scatter', fill: null };
|
||||
let up = { x: v.x_axis, y: v.upload, name: "UL", type: 'scatter', fill: null };
|
||||
graphData.push(dn);
|
||||
graphData.push(up);*/
|
||||
|
||||
let graph = document.getElementById(target_div);
|
||||
Plotly.newPlot(
|
||||
graph,
|
||||
@ -246,6 +242,24 @@ class MultiRingBuffer {
|
||||
},
|
||||
{ responsive: true, displayModeBar: false });
|
||||
}
|
||||
|
||||
plotTotalThroughput(target_div) {
|
||||
let graph = document.getElementById(target_div);
|
||||
|
||||
let totalDown = yValsRingSort(this.data['total'].download, this.data['total'].head, this.data['total'].capacity);
|
||||
let totalUp = yValsRingSort(this.data['total'].upload, this.data['total'].head, this.data['total'].capacity);
|
||||
let shapedDown = yValsRingSort(this.data['shaped'].download, this.data['shaped'].head, this.data['shaped'].capacity);
|
||||
let shapedUp = yValsRingSort(this.data['shaped'].upload, this.data['shaped'].head, this.data['shaped'].capacity);
|
||||
let x = this.data['total'].x_axis;
|
||||
|
||||
let data = [
|
||||
{x: x, y:totalDown, name: 'Download', type: 'scatter', marker: {color: 'rgb(255,160,122)'}},
|
||||
{x: x, y:totalUp, name: 'Upload', type: 'scatter', marker: {color: 'rgb(255,160,122)'}},
|
||||
{x: x, y:shapedDown, name: 'Shaped Download', type: 'scatter', fill: 'tozeroy', marker: {color: 'rgb(124,252,0)'}},
|
||||
{x: x, y:shapedUp, name: 'Shaped Upload', type: 'scatter', fill: 'tozeroy', marker: {color: 'rgb(124,252,0)'}},
|
||||
];
|
||||
Plotly.newPlot(graph, data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true, title: "Time since now (seconds)"} }, { responsive: true });
|
||||
}
|
||||
}
|
||||
|
||||
class RingBuffer {
|
||||
|
@ -159,40 +159,21 @@
|
||||
<footer>© 2022-2023, LibreQoE LLC</footer>
|
||||
|
||||
<script>
|
||||
let throughput = new MultiRingBuffer(300);
|
||||
|
||||
function updateCurrentThroughput() {
|
||||
$.get("/api/current_throughput", (tp) => {
|
||||
$("#ppsDown").text(scaleNumber(tp.packets_per_second[0]));
|
||||
$("#ppsUp").text(scaleNumber(tp.packets_per_second[1]));
|
||||
$("#bpsDown").text(scaleNumber(tp.bits_per_second[0]));
|
||||
$("#bpsUp").text(scaleNumber(tp.bits_per_second[1]));
|
||||
setTimeout(updateCurrentThroughput, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function updateThroughputGraph() {
|
||||
$.get("/api/throughput_ring", (tp) => {
|
||||
let graph = document.getElementById("tpGraph");
|
||||
let x = [];
|
||||
let y = []; // Down
|
||||
let y2 = []; // Up
|
||||
let y3 = []; // Shaped Down
|
||||
let y4 = []; // Shaped Up
|
||||
for (i=0; i<300; i++) {
|
||||
x.push(i);
|
||||
y.push(tp[i].bits_per_second[0]);
|
||||
y2.push(0.0 - tp[i].bits_per_second[1]);
|
||||
y3.push(tp[i].shaped_bits_per_second[0]);
|
||||
y4.push(0.0 - tp[i].shaped_bits_per_second[1]);
|
||||
}
|
||||
let data = [
|
||||
{x: x, y:y, name: 'Download', type: 'scatter'},
|
||||
{x: x, y:y2, name: 'Upload', type: 'scatter'},
|
||||
{x: x, y:y3, name: 'Shaped Download', type: 'scatter', fill: 'tozeroy'},
|
||||
{x: x, y:y4, name: 'Shaped Upload', type: 'scatter', fill: 'tozeroy'},
|
||||
];
|
||||
Plotly.newPlot(graph, data, { margin: { l:0,r:0,b:0,t:0,pad:4 }, yaxis: { automargin: true }, xaxis: {automargin: true, title: "Time since now (seconds)"} }, { responsive: true });
|
||||
//console.log(tp);
|
||||
setTimeout(updateThroughputGraph, 1000);
|
||||
throughput.push("pps", tp.packets_per_second[0], tp.packets_per_second[1]);
|
||||
throughput.push("total", tp.bits_per_second[0], tp.bits_per_second[1]);
|
||||
throughput.push("shaped", tp.shaped_bits_per_second[0], tp.shaped_bits_per_second[1]);
|
||||
throughput.plotTotalThroughput("tpGraph");
|
||||
|
||||
setTimeout(updateCurrentThroughput, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
@ -321,14 +302,13 @@
|
||||
|
||||
colorReloadButton();
|
||||
updateCurrentThroughput();
|
||||
updateThroughputGraph();
|
||||
updateCpu();
|
||||
updateRam();
|
||||
updateTop10();
|
||||
updateWorst10();
|
||||
updateHistogram();
|
||||
updateHostCounts();
|
||||
updateThroughputQuantile();
|
||||
//updateThroughputQuantile();
|
||||
}
|
||||
|
||||
$(document).ready(start);
|
||||
|
Loading…
Reference in New Issue
Block a user