mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-14 03:49:19 +01:00
feat: reworker worker task, allows to change db
now way more modularized and better error checked. allows receiving db uris from a mpsc channel, and reconnects to them
This commit is contained in:
parent
f48d1e3682
commit
4345a9e9b9
1 changed files with 207 additions and 190 deletions
|
@ -1,7 +1,7 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sea_orm::{TransactionTrait, DatabaseConnection, EntityTrait, Condition, ColumnTrait, QueryFilter, Set, QueryOrder, Order, ActiveModelTrait, ActiveValue::{NotSet, self}};
|
use sea_orm::{TransactionTrait, DatabaseConnection, EntityTrait, Condition, ColumnTrait, QueryFilter, Set, QueryOrder, Order, ActiveModelTrait, ActiveValue::{NotSet, self}, Database, DbErr};
|
||||||
use tokio::sync::{watch, mpsc};
|
use tokio::sync::{watch, mpsc};
|
||||||
use tracing::{info, error};
|
use tracing::{info, error, warn};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::data::{entities, FetchError};
|
use crate::data::{entities, FetchError};
|
||||||
|
@ -37,6 +37,8 @@ struct AppStateTransmitters {
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
tx: AppStateTransmitters,
|
tx: AppStateTransmitters,
|
||||||
|
|
||||||
|
db_uri: mpsc::Receiver<String>,
|
||||||
|
|
||||||
panels: Vec<entities::panels::Model>,
|
panels: Vec<entities::panels::Model>,
|
||||||
sources: Vec<entities::sources::Model>,
|
sources: Vec<entities::sources::Model>,
|
||||||
metrics: Vec<entities::metrics::Model>,
|
metrics: Vec<entities::metrics::Model>,
|
||||||
|
@ -53,6 +55,7 @@ pub struct AppState {
|
||||||
cache_age: i64,
|
cache_age: i64,
|
||||||
|
|
||||||
width: watch::Receiver<i64>,
|
width: watch::Receiver<i64>,
|
||||||
|
last_width: i64,
|
||||||
|
|
||||||
view: AppStateView,
|
view: AppStateView,
|
||||||
}
|
}
|
||||||
|
@ -66,6 +69,7 @@ async fn sleep(t:i64) {
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
width: watch::Receiver<i64>,
|
width: watch::Receiver<i64>,
|
||||||
|
db_uri: mpsc::Receiver<String>,
|
||||||
interval: i64,
|
interval: i64,
|
||||||
cache_age: i64,
|
cache_age: i64,
|
||||||
) -> Result<AppState, FetchError> {
|
) -> Result<AppState, FetchError> {
|
||||||
|
@ -86,6 +90,7 @@ impl AppState {
|
||||||
last_refresh: 0,
|
last_refresh: 0,
|
||||||
points: VecDeque::new(),
|
points: VecDeque::new(),
|
||||||
last_check: 0,
|
last_check: 0,
|
||||||
|
last_width: 0,
|
||||||
flush: flush_rx,
|
flush: flush_rx,
|
||||||
op: op_rx,
|
op: op_rx,
|
||||||
view: AppStateView {
|
view: AppStateView {
|
||||||
|
@ -105,6 +110,7 @@ impl AppState {
|
||||||
panel_metric: panel_metric_tx,
|
panel_metric: panel_metric_tx,
|
||||||
},
|
},
|
||||||
width,
|
width,
|
||||||
|
db_uri,
|
||||||
interval,
|
interval,
|
||||||
cache_age,
|
cache_age,
|
||||||
})
|
})
|
||||||
|
@ -143,7 +149,6 @@ impl AppState {
|
||||||
error!(target: "worker", "Could not send panel-metric update: {:?}", e);
|
error!(target: "worker", "Could not send panel-metric update: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(target: "worker", "Updated panels, sources and metrics");
|
|
||||||
self.last_refresh = chrono::Utc::now().timestamp();
|
self.last_refresh = chrono::Utc::now().timestamp();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -152,16 +157,7 @@ impl AppState {
|
||||||
chrono::Utc::now().timestamp() - self.last_refresh
|
chrono::Utc::now().timestamp() - self.last_refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn worker(mut self, db: DatabaseConnection, run:watch::Receiver<bool>) {
|
pub async fn parse_op(&mut self, op:BackgroundAction, db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||||
let mut width = *self.width.borrow() * 60; // TODO it's in minutes somewhere...
|
|
||||||
let mut last = Utc::now().timestamp() - width;
|
|
||||||
|
|
||||||
while *run.borrow() {
|
|
||||||
let now = Utc::now().timestamp();
|
|
||||||
tokio::select!{
|
|
||||||
op = self.op.recv() => {
|
|
||||||
match op {
|
|
||||||
Some(op) => {
|
|
||||||
match op {
|
match op {
|
||||||
BackgroundAction::UpdateAllPanels { panels } => {
|
BackgroundAction::UpdateAllPanels { panels } => {
|
||||||
// TODO this is kinda rough, can it be done better?
|
// TODO this is kinda rough, can it be done better?
|
||||||
|
@ -200,11 +196,9 @@ impl AppState {
|
||||||
ActiveValue::Unchanged(pid) => Some(pid),
|
ActiveValue::Unchanged(pid) => Some(pid),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let op = if panel.id == NotSet { panel.insert(&db) } else { panel.update(&db) };
|
let op = if panel.id == NotSet { panel.insert(db) } else { panel.update(db) };
|
||||||
|
op.await?;
|
||||||
// TODO chained if is trashy
|
// TODO chained if is trashy
|
||||||
if let Err(e) = op.await {
|
|
||||||
error!(target: "worker", "Could not update panel: {:?}", e);
|
|
||||||
} else {
|
|
||||||
if let Some(panel_id) = panel_id {
|
if let Some(panel_id) = panel_id {
|
||||||
if let Err(e) = db.transaction::<_, (), sea_orm::DbErr>(|txn| {
|
if let Err(e) = db.transaction::<_, (), sea_orm::DbErr>(|txn| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
@ -223,18 +217,14 @@ impl AppState {
|
||||||
} else {
|
} else {
|
||||||
self.view.request_flush().await;
|
self.view.request_flush().await;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
BackgroundAction::UpdateSource { source } => {
|
BackgroundAction::UpdateSource { source } => {
|
||||||
let op = if source.id == NotSet { source.insert(&db) } else { source.update(&db) };
|
let op = if source.id == NotSet { source.insert(db) } else { source.update(db) };
|
||||||
if let Err(e) = op.await {
|
op.await?;
|
||||||
error!(target: "worker", "Could not update source: {:?}", e);
|
|
||||||
} else {
|
|
||||||
self.view.request_flush().await;
|
self.view.request_flush().await;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
BackgroundAction::UpdateMetric { metric } => {
|
BackgroundAction::UpdateMetric { metric } => {
|
||||||
let op = if metric.id == NotSet { metric.insert(&db) } else { metric.update(&db) };
|
let op = if metric.id == NotSet { metric.insert(db) } else { metric.update(db) };
|
||||||
if let Err(e) = op.await {
|
if let Err(e) = op.await {
|
||||||
error!(target: "worker", "Could not update metric: {:?}", e);
|
error!(target: "worker", "Could not update metric: {:?}", e);
|
||||||
} else {
|
} else {
|
||||||
|
@ -243,64 +233,46 @@ impl AppState {
|
||||||
},
|
},
|
||||||
// _ => todo!(),
|
// _ => todo!(),
|
||||||
}
|
}
|
||||||
},
|
Ok(())
|
||||||
None => {},
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ = self.flush.recv() => {
|
pub async fn flush_data(&mut self, db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||||
let now = Utc::now().timestamp();
|
let now = Utc::now().timestamp();
|
||||||
if let Err(e) = self.fetch(&db).await {
|
self.fetch(db).await?;
|
||||||
error!(target: "worker", "Could not fetch from db: {:?}", e);
|
|
||||||
}
|
|
||||||
let new_width = *self.width.borrow() * 60; // TODO it's in minutes somewhere...
|
let new_width = *self.width.borrow() * 60; // TODO it's in minutes somewhere...
|
||||||
self.points = match entities::points::Entity::find()
|
self.points = entities::points::Entity::find()
|
||||||
.filter(
|
.filter(
|
||||||
Condition::all()
|
Condition::all()
|
||||||
.add(entities::points::Column::X.gte((now - new_width) as f64))
|
.add(entities::points::Column::X.gte((now - new_width) as f64))
|
||||||
.add(entities::points::Column::X.lte(now as f64))
|
.add(entities::points::Column::X.lte(now as f64))
|
||||||
)
|
)
|
||||||
.order_by(entities::points::Column::X, Order::Asc)
|
.order_by(entities::points::Column::X, Order::Asc)
|
||||||
.all(&db)
|
.all(db)
|
||||||
.await {
|
.await?.into();
|
||||||
Ok(p) => p.into(),
|
|
||||||
Err(e) => {
|
|
||||||
error!(target: "worker", "Could not fetch new points: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(e) = self.tx.points.send(self.points.clone().into()) {
|
if let Err(e) = self.tx.points.send(self.points.clone().into()) {
|
||||||
error!(target: "worker", "Could not send new points: {:?}", e);
|
warn!(target: "worker", "Could not send new points: {:?}", e); // TODO should be an err?
|
||||||
}
|
}
|
||||||
last = Utc::now().timestamp();
|
self.last_check = Utc::now().timestamp() - *self.width.borrow();
|
||||||
info!(target: "worker", "Reloaded points");
|
info!(target: "worker", "Reloaded points");
|
||||||
},
|
Ok(())
|
||||||
_ = sleep(self.cache_age - (now - self.last_refresh)) => {
|
|
||||||
if let Err(e) = self.fetch(&db).await {
|
|
||||||
error!(target: "worker", "Could not fetch from db: {:?}", e);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ = sleep(self.interval - (now - self.last_check)) => {
|
pub async fn update_points(&mut self, db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||||
let mut changes = false;
|
let mut changes = false;
|
||||||
let now = Utc::now().timestamp();
|
let now = Utc::now().timestamp();
|
||||||
let new_width = *self.width.borrow() * 60; // TODO it's in minutes somewhere...
|
let new_width = *self.width.borrow() * 60; // TODO it's in minutes somewhere...
|
||||||
|
|
||||||
// fetch previous points
|
// fetch previous points
|
||||||
if new_width != width {
|
if new_width != self.last_width {
|
||||||
let mut previous_points = match entities::points::Entity::find()
|
let mut previous_points = entities::points::Entity::find()
|
||||||
.filter(
|
.filter(
|
||||||
Condition::all()
|
Condition::all()
|
||||||
.add(entities::points::Column::X.gte(now - new_width))
|
.add(entities::points::Column::X.gte(now - new_width))
|
||||||
.add(entities::points::Column::X.lte(now - width))
|
.add(entities::points::Column::X.lte(now - self.last_width))
|
||||||
)
|
)
|
||||||
.order_by(entities::points::Column::X, Order::Asc)
|
.order_by(entities::points::Column::X, Order::Asc)
|
||||||
.all(&db)
|
.all(db)
|
||||||
.await {
|
.await?;
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
error!(target: "worker", "Could not fetch previous points: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if previous_points.len() > 0 {
|
if previous_points.len() > 0 {
|
||||||
info!(target: "worker", "Fetched {} previous points", previous_points.len());
|
info!(target: "worker", "Fetched {} previous points", previous_points.len());
|
||||||
}
|
}
|
||||||
|
@ -312,24 +284,15 @@ impl AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch new points
|
// fetch new points
|
||||||
let new_points = match entities::points::Entity::find()
|
let new_points = entities::points::Entity::find()
|
||||||
.filter(
|
.filter(
|
||||||
Condition::all()
|
Condition::all()
|
||||||
.add(entities::points::Column::X.gte(last as f64))
|
.add(entities::points::Column::X.gte(self.last_check as f64))
|
||||||
.add(entities::points::Column::X.lte(now as f64))
|
.add(entities::points::Column::X.lte(now as f64))
|
||||||
)
|
)
|
||||||
.order_by(entities::points::Column::X, Order::Asc)
|
.order_by(entities::points::Column::X, Order::Asc)
|
||||||
.all(&db)
|
.all(db)
|
||||||
.await {
|
.await?;
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
error!(target: "worker", "Could not fetch new points: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if new_points.len() > 0 {
|
|
||||||
info!(target: "worker", "Fetched {} new points", new_points.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
for p in new_points {
|
for p in new_points {
|
||||||
self.points.push_back(p);
|
self.points.push_back(p);
|
||||||
|
@ -346,16 +309,70 @@ impl AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update
|
// update
|
||||||
last = now;
|
self.last_width = new_width;
|
||||||
width = new_width;
|
|
||||||
self.last_check = now;
|
self.last_check = now;
|
||||||
if changes {
|
if changes {
|
||||||
if let Err(e) = self.tx.points.send(self.points.clone().into()) {
|
if let Err(e) = self.tx.points.send(self.points.clone().into()) {
|
||||||
error!(target: "worker", "Could not send changes to main thread: {:?}", e);
|
warn!(target: "worker", "Could not send changes to main thread: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn worker(mut self, run:watch::Receiver<bool>) {
|
||||||
|
let mut now;
|
||||||
|
let first_db_uri = self.db_uri.recv().await.unwrap();
|
||||||
|
|
||||||
|
let mut db = Database::connect(first_db_uri.clone()).await.unwrap();
|
||||||
|
|
||||||
|
info!(target: "worker", "Connected to '{}'", first_db_uri);
|
||||||
|
|
||||||
|
while *run.borrow() {
|
||||||
|
now = Utc::now().timestamp();
|
||||||
|
tokio::select!{
|
||||||
|
res = self.db_uri.recv() => {
|
||||||
|
match res {
|
||||||
|
Some(uri) => {
|
||||||
|
match Database::connect(uri.clone()).await {
|
||||||
|
Ok(new_db) => {
|
||||||
|
info!("Connected to '{}'", uri);
|
||||||
|
db = new_db;
|
||||||
|
},
|
||||||
|
Err(e) => error!(target: "worker", "Could not connect to db: {:?}", e),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
None => { error!(target: "worker", "URI channel closed"); break; },
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
res = self.op.recv() => {
|
||||||
|
match res {
|
||||||
|
Some(op) => match self.parse_op(op, &db).await {
|
||||||
|
Ok(()) => { },
|
||||||
|
Err(e) => error!(target: "worker", "Failed executing operation: {:?}", e),
|
||||||
|
},
|
||||||
|
None => { error!(target: "worker", "Operations channel closed"); break; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = self.flush.recv() => {
|
||||||
|
match res {
|
||||||
|
Some(()) => match self.flush_data(&db).await {
|
||||||
|
Ok(()) => { },
|
||||||
|
Err(e) => error!(target: "worker", "Could not flush away current data: {:?}", e),
|
||||||
|
},
|
||||||
|
None => { error!(target: "worker", "Flush channel closed"); break; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = sleep(self.cache_age - (now - self.last_refresh)) => {
|
||||||
|
if let Err(e) = self.fetch(&db).await {
|
||||||
|
error!(target: "worker", "Could not fetch from db: {:?}", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = sleep(self.interval - (now - self.last_check)) => {
|
||||||
|
if let Err(e) = self.update_points(&db).await {
|
||||||
|
error!(target: "worker", "Could not update points: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue