Add http apis to query status, add and remove channel
This commit is contained in:
@@ -29,7 +29,9 @@ libc = "0.2"
|
||||
regex = "1.5.5"
|
||||
axum = "0.5"
|
||||
http = "0.2"
|
||||
url = "2.2"
|
||||
hyper = "0.14"
|
||||
chrono = "0.4"
|
||||
log = { path = "../log" }
|
||||
stats = { path = "../stats" }
|
||||
err = { path = "../../daqbuffer/err" }
|
||||
|
||||
+249
-94
@@ -15,7 +15,7 @@ use netpod::{Database, ScyllaConfig};
|
||||
use scylla::batch::Consistency;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stats::{CaConnStats, CaConnStatsAgg, CaConnStatsAggDiff};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
@@ -23,6 +23,8 @@ use std::sync::{Arc, Mutex, Once};
|
||||
use std::time::Duration;
|
||||
use tokio::fs::OpenOptions;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::sync::Mutex as TokMx;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_postgres::Client as PgClient;
|
||||
|
||||
static mut METRICS: Option<Mutex<Option<CaConnStatsAgg>>> = None;
|
||||
@@ -40,6 +42,7 @@ pub fn get_metrics() -> &'static mut Option<CaConnStatsAgg> {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ChannelConfig {
|
||||
backend: String,
|
||||
channels: Vec<String>,
|
||||
search: Vec<String>,
|
||||
addr_bind: Ipv4Addr,
|
||||
@@ -87,6 +90,7 @@ pub async fn parse_config(config: PathBuf) -> Result<CaConnectOpts, Error> {
|
||||
})
|
||||
.collect();
|
||||
Ok(CaConnectOpts {
|
||||
backend: conf.backend,
|
||||
channels: conf.channels,
|
||||
search: conf.search,
|
||||
addr_bind: conf.addr_bind,
|
||||
@@ -106,6 +110,7 @@ pub async fn parse_config(config: PathBuf) -> Result<CaConnectOpts, Error> {
|
||||
}
|
||||
|
||||
pub struct CaConnectOpts {
|
||||
pub backend: String,
|
||||
pub channels: Vec<String>,
|
||||
pub search: Vec<String>,
|
||||
pub addr_bind: Ipv4Addr,
|
||||
@@ -127,7 +132,7 @@ async fn spawn_scylla_insert_workers(
|
||||
scyconf: ScyllaConfig,
|
||||
insert_scylla_sessions: usize,
|
||||
insert_worker_count: usize,
|
||||
insert_item_queue: &CommonInsertItemQueue,
|
||||
insert_item_queue: Arc<CommonInsertItemQueue>,
|
||||
insert_frac: Arc<AtomicU64>,
|
||||
pg_client: Arc<PgClient>,
|
||||
store_stats: Arc<stats::CaConnStats>,
|
||||
@@ -232,42 +237,135 @@ async fn spawn_scylla_insert_workers(
|
||||
}
|
||||
|
||||
pub struct CommandQueueSet {
|
||||
queues: tokio::sync::Mutex<VecDeque<Sender<ConnCommand>>>,
|
||||
queues: tokio::sync::Mutex<BTreeMap<SocketAddrV4, Sender<ConnCommand>>>,
|
||||
}
|
||||
|
||||
impl CommandQueueSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
queues: tokio::sync::Mutex::new(VecDeque::<Sender<ConnCommand>>::new()),
|
||||
queues: tokio::sync::Mutex::new(BTreeMap::<SocketAddrV4, Sender<ConnCommand>>::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queues(&self) -> &tokio::sync::Mutex<VecDeque<Sender<ConnCommand>>> {
|
||||
pub fn queues(&self) -> &tokio::sync::Mutex<BTreeMap<SocketAddrV4, Sender<ConnCommand>>> {
|
||||
&self.queues
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IngestCommons {
|
||||
pub pgconf: Arc<Database>,
|
||||
pub local_epics_hostname: String,
|
||||
pub insert_item_queue: Arc<CommonInsertItemQueue>,
|
||||
pub data_store: Arc<DataStore>,
|
||||
pub insert_ivl_min: Arc<AtomicU64>,
|
||||
pub conn_stats: Arc<TokMx<Vec<Arc<CaConnStats>>>>,
|
||||
pub command_queue_set: Arc<CommandQueueSet>,
|
||||
}
|
||||
|
||||
pub async fn find_channel_addr(
|
||||
backend: String,
|
||||
name: String,
|
||||
pgconf: &Database,
|
||||
) -> Result<Option<SocketAddrV4>, Error> {
|
||||
// TODO also here, provide a db pool.
|
||||
let d = pgconf;
|
||||
let (pg_client, pg_conn) = tokio_postgres::connect(
|
||||
&format!("postgresql://{}:{}@{}:{}/{}", d.user, d.pass, d.host, d.port, d.name),
|
||||
tokio_postgres::tls::NoTls,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
// TODO allow clean shutdown on ctrl-c and join the pg_conn in the end:
|
||||
tokio::spawn(pg_conn);
|
||||
let pg_client = Arc::new(pg_client);
|
||||
let qu_find_addr = pg_client
|
||||
.prepare("with q1 as (select t1.facility, t1.channel, t1.addr from ioc_by_channel t1 where t1.facility = $1 and t1.channel in ($2) and t1.addr != '' order by t1.tsmod desc) select distinct on (q1.facility, q1.channel) q1.facility, q1.channel, q1.addr from q1")
|
||||
.await
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("{e:?}")))?;
|
||||
let rows = pg_client
|
||||
.query(&qu_find_addr, &[&backend, &name])
|
||||
.await
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("pg lookup error: {e:?}")))?;
|
||||
if rows.is_empty() {
|
||||
error!("can not find any addresses of channels {:?}", name);
|
||||
} else {
|
||||
for row in rows {
|
||||
let addr: &str = row.get(2);
|
||||
if addr == "" {
|
||||
return Ok(None);
|
||||
} else {
|
||||
match addr.parse::<SocketAddrV4>() {
|
||||
Ok(addr) => return Ok(Some(addr)),
|
||||
Err(_) => return Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn create_ca_conn(
|
||||
addr: SocketAddrV4,
|
||||
local_epics_hostname: String,
|
||||
array_truncate: usize,
|
||||
insert_queue_max: usize,
|
||||
insert_item_queue: Arc<CommonInsertItemQueue>,
|
||||
data_store: Arc<DataStore>,
|
||||
insert_ivl_min: Arc<AtomicU64>,
|
||||
conn_stats: Arc<TokMx<Vec<Arc<CaConnStats>>>>,
|
||||
command_queue_set: Arc<CommandQueueSet>,
|
||||
) -> Result<JoinHandle<Result<(), Error>>, Error> {
|
||||
info!("create new CaConn {:?}", addr);
|
||||
let data_store = data_store.clone();
|
||||
let conn = CaConn::new(
|
||||
addr,
|
||||
local_epics_hostname,
|
||||
data_store.clone(),
|
||||
insert_item_queue.sender(),
|
||||
array_truncate,
|
||||
insert_queue_max,
|
||||
insert_ivl_min.clone(),
|
||||
);
|
||||
conn_stats.lock().await.push(conn.stats());
|
||||
let stats2 = conn.stats();
|
||||
let conn_command_tx = conn.conn_command_tx();
|
||||
{
|
||||
command_queue_set.queues().lock().await.insert(addr, conn_command_tx);
|
||||
}
|
||||
let conn_block = async move {
|
||||
let mut conn = conn;
|
||||
while let Some(item) = conn.next().await {
|
||||
match item {
|
||||
Ok(_) => {
|
||||
stats2.conn_item_count_inc();
|
||||
}
|
||||
Err(e) => {
|
||||
error!("CaConn gives error: {e:?}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<_, Error>(())
|
||||
};
|
||||
let jh = tokio::spawn(conn_block);
|
||||
Ok(jh)
|
||||
}
|
||||
|
||||
pub async fn ca_connect(opts: ListenFromFileOpts) -> Result<(), Error> {
|
||||
let facility = "scylla";
|
||||
let insert_frac = Arc::new(AtomicU64::new(1000));
|
||||
let insert_ivl_min = Arc::new(AtomicU64::new(8800));
|
||||
let opts = parse_config(opts.config).await?;
|
||||
let scyconf = opts.scyconf.clone();
|
||||
|
||||
let command_queue_set = Arc::new(CommandQueueSet::new());
|
||||
tokio::spawn(crate::metrics::start_metrics_service(
|
||||
opts.api_bind.clone(),
|
||||
insert_frac.clone(),
|
||||
insert_ivl_min.clone(),
|
||||
command_queue_set.clone(),
|
||||
));
|
||||
let d = Database {
|
||||
let pgconf = Database {
|
||||
name: opts.pgconf.name.clone(),
|
||||
host: opts.pgconf.host.clone(),
|
||||
port: opts.pgconf.port.clone(),
|
||||
user: opts.pgconf.user.clone(),
|
||||
pass: opts.pgconf.pass.clone(),
|
||||
};
|
||||
|
||||
let d = &pgconf;
|
||||
let (pg_client, pg_conn) = tokio_postgres::connect(
|
||||
&format!("postgresql://{}:{}@{}:{}/{}", d.user, d.pass, d.host, d.port, d.name),
|
||||
tokio_postgres::tls::NoTls,
|
||||
@@ -294,6 +392,74 @@ pub async fn ca_connect(opts: ListenFromFileOpts) -> Result<(), Error> {
|
||||
.await
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("{e:?}")))?;
|
||||
let mut channels_by_host = BTreeMap::new();
|
||||
|
||||
let data_store = Arc::new(DataStore::new(pg_client.clone(), scy.clone()).await?);
|
||||
let insert_item_queue = CommonInsertItemQueue::new(opts.insert_item_queue_cap);
|
||||
let insert_item_queue = Arc::new(insert_item_queue);
|
||||
// TODO use a new stats struct
|
||||
let store_stats = Arc::new(CaConnStats::new());
|
||||
spawn_scylla_insert_workers(
|
||||
opts.scyconf.clone(),
|
||||
opts.insert_scylla_sessions,
|
||||
opts.insert_worker_count,
|
||||
insert_item_queue.clone(),
|
||||
insert_frac.clone(),
|
||||
pg_client.clone(),
|
||||
store_stats.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut conn_jhs = vec![];
|
||||
let conn_stats: Arc<TokMx<Vec<Arc<CaConnStats>>>> = Arc::new(TokMx::new(Vec::new()));
|
||||
let command_queue_set = Arc::new(CommandQueueSet::new());
|
||||
|
||||
let ingest_commons = IngestCommons {
|
||||
pgconf: Arc::new(pgconf.clone()),
|
||||
local_epics_hostname: opts.local_epics_hostname.clone(),
|
||||
insert_item_queue: insert_item_queue.clone(),
|
||||
data_store: data_store.clone(),
|
||||
insert_ivl_min: insert_ivl_min.clone(),
|
||||
conn_stats: conn_stats.clone(),
|
||||
command_queue_set: command_queue_set.clone(),
|
||||
};
|
||||
let ingest_commons = Arc::new(ingest_commons);
|
||||
|
||||
tokio::spawn(crate::metrics::start_metrics_service(
|
||||
opts.api_bind.clone(),
|
||||
insert_frac.clone(),
|
||||
insert_ivl_min.clone(),
|
||||
command_queue_set.clone(),
|
||||
ingest_commons.clone(),
|
||||
));
|
||||
|
||||
let metrics_agg_fut = {
|
||||
let conn_stats = conn_stats.clone();
|
||||
let local_stats = local_stats.clone();
|
||||
async move {
|
||||
let mut agg_last = CaConnStatsAgg::new();
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_millis(671)).await;
|
||||
let agg = CaConnStatsAgg::new();
|
||||
agg.push(&local_stats);
|
||||
agg.push(&store_stats);
|
||||
for g in conn_stats.lock().await.iter() {
|
||||
agg.push(&g);
|
||||
}
|
||||
let m = get_metrics();
|
||||
*m = Some(agg.clone());
|
||||
if false {
|
||||
let diff = CaConnStatsAggDiff::diff_from(&agg_last, &agg);
|
||||
info!("{}", diff.display());
|
||||
}
|
||||
agg_last = agg;
|
||||
if false {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let metrics_agg_jh = tokio::spawn(metrics_agg_fut);
|
||||
|
||||
let mut chns_todo = &opts.channels[..];
|
||||
let mut chstmp = ["__NONE__"; 8];
|
||||
let mut ix = 0;
|
||||
@@ -306,7 +472,14 @@ pub async fn ca_connect(opts: ListenFromFileOpts) -> Result<(), Error> {
|
||||
.query(
|
||||
&qu_find_addr,
|
||||
&[
|
||||
&facility, &chstmp[0], &chstmp[1], &chstmp[2], &chstmp[3], &chstmp[4], &chstmp[5], &chstmp[6],
|
||||
&opts.backend,
|
||||
&chstmp[0],
|
||||
&chstmp[1],
|
||||
&chstmp[2],
|
||||
&chstmp[3],
|
||||
&chstmp[4],
|
||||
&chstmp[5],
|
||||
&chstmp[6],
|
||||
&chstmp[7],
|
||||
],
|
||||
)
|
||||
@@ -340,90 +513,71 @@ pub async fn ca_connect(opts: ListenFromFileOpts) -> Result<(), Error> {
|
||||
} else {
|
||||
channels_by_host.get_mut(&addr).unwrap().push(ch.to_string());
|
||||
}
|
||||
{
|
||||
let create_new = {
|
||||
let g = command_queue_set.queues().lock().await;
|
||||
if let Some(tx) = g.get(&addr) {
|
||||
let (cmd, rx) = ConnCommand::channel_add(ch.to_string());
|
||||
tx.send(cmd).await.unwrap();
|
||||
if !rx.recv().await.unwrap() {
|
||||
error!("Could not add channel: {}", ch);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
if create_new {
|
||||
info!("create new CaConn {:?} {:?}", addr, ch);
|
||||
let data_store = data_store.clone();
|
||||
let conn = CaConn::new(
|
||||
addr,
|
||||
opts.local_epics_hostname.clone(),
|
||||
data_store.clone(),
|
||||
insert_item_queue.sender(),
|
||||
opts.array_truncate,
|
||||
opts.insert_queue_max,
|
||||
insert_ivl_min.clone(),
|
||||
);
|
||||
conn_stats.lock().await.push(conn.stats());
|
||||
let stats2 = conn.stats();
|
||||
let conn_command_tx = conn.conn_command_tx();
|
||||
let tx = conn_command_tx.clone();
|
||||
{
|
||||
command_queue_set.queues().lock().await.insert(addr, conn_command_tx);
|
||||
}
|
||||
let conn_block = async move {
|
||||
let mut conn = conn;
|
||||
while let Some(item) = conn.next().await {
|
||||
match item {
|
||||
Ok(_) => {
|
||||
stats2.conn_item_count_inc();
|
||||
}
|
||||
Err(e) => {
|
||||
error!("CaConn gives error: {e:?}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<_, Error>(())
|
||||
};
|
||||
let jh = tokio::spawn(conn_block);
|
||||
conn_jhs.push(jh);
|
||||
{
|
||||
let (cmd, rx) = ConnCommand::channel_add(ch.to_string());
|
||||
tx.send(cmd).await.unwrap();
|
||||
if !rx.recv().await.unwrap() {
|
||||
error!("Could not add channel: {}", ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if opts.abort_after_search == 1 {
|
||||
return Ok(());
|
||||
}
|
||||
let data_store = Arc::new(DataStore::new(pg_client.clone(), scy.clone()).await?);
|
||||
let insert_item_queue = CommonInsertItemQueue::new(opts.insert_item_queue_cap);
|
||||
|
||||
// TODO use a new stats struct
|
||||
let store_stats = Arc::new(CaConnStats::new());
|
||||
|
||||
spawn_scylla_insert_workers(
|
||||
opts.scyconf.clone(),
|
||||
opts.insert_scylla_sessions,
|
||||
opts.insert_worker_count,
|
||||
&insert_item_queue,
|
||||
insert_frac.clone(),
|
||||
pg_client.clone(),
|
||||
store_stats.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut conn_jhs = vec![];
|
||||
let mut conn_stats = vec![];
|
||||
info!("channels_by_host len {}", channels_by_host.len());
|
||||
for (host, channels) in channels_by_host {
|
||||
let data_store = data_store.clone();
|
||||
let addr = SocketAddrV4::new(host.ip().clone(), host.port());
|
||||
let mut conn = CaConn::new(
|
||||
addr,
|
||||
opts.local_epics_hostname.clone(),
|
||||
data_store.clone(),
|
||||
insert_item_queue.sender(),
|
||||
opts.array_truncate,
|
||||
opts.insert_queue_max,
|
||||
insert_ivl_min.clone(),
|
||||
);
|
||||
conn_stats.push(conn.stats());
|
||||
for c in channels {
|
||||
conn.channel_add(c);
|
||||
}
|
||||
let stats2 = conn.stats();
|
||||
let conn_command_tx = conn.conn_command_tx();
|
||||
command_queue_set.queues().lock().await.push_back(conn_command_tx);
|
||||
let conn_block = async move {
|
||||
while let Some(item) = conn.next().await {
|
||||
match item {
|
||||
Ok(_) => {
|
||||
stats2.conn_item_count_inc();
|
||||
}
|
||||
Err(e) => {
|
||||
error!("CaConn gives error: {e:?}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<_, Error>(())
|
||||
};
|
||||
let jh = tokio::spawn(conn_block);
|
||||
conn_jhs.push(jh);
|
||||
}
|
||||
let mut agg_last = CaConnStatsAgg::new();
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_millis(671)).await;
|
||||
let agg = CaConnStatsAgg::new();
|
||||
agg.push(&local_stats);
|
||||
agg.push(&store_stats);
|
||||
for g in &conn_stats {
|
||||
agg.push(&g);
|
||||
}
|
||||
let m = get_metrics();
|
||||
*m = Some(agg.clone());
|
||||
if false {
|
||||
let diff = CaConnStatsAggDiff::diff_from(&agg_last, &agg);
|
||||
info!("{}", diff.display());
|
||||
}
|
||||
for _s1 in &conn_stats {}
|
||||
agg_last = agg;
|
||||
if false {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for jh in conn_jhs {
|
||||
match jh.await {
|
||||
Ok(k) => match k {
|
||||
@@ -437,5 +591,6 @@ pub async fn ca_connect(opts: ListenFromFileOpts) -> Result<(), Error> {
|
||||
}
|
||||
}
|
||||
}
|
||||
metrics_agg_jh.await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+216
-21
@@ -13,6 +13,7 @@ use libc::c_int;
|
||||
use log::*;
|
||||
use netpod::timeunits::*;
|
||||
use netpod::{ScalarType, Shape};
|
||||
use serde::Serialize;
|
||||
use stats::{CaConnStats, IntervalEma};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
@@ -24,18 +25,60 @@ use std::time::{Duration, Instant, SystemTime};
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ChannelStateInfo {
|
||||
pub name: String,
|
||||
pub scalar_type: Option<ScalarType>,
|
||||
pub shape: Option<Shape>,
|
||||
// NOTE: this solution can yield to the same Instant serialize to different string representations.
|
||||
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "ser_instant")]
|
||||
pub ts_created: Option<Instant>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "ser_instant")]
|
||||
pub ts_event_last: Option<Instant>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub item_recv_ivl_ema: Option<f32>,
|
||||
pub interest_score: f32,
|
||||
}
|
||||
|
||||
fn ser_instant<S: serde::Serializer>(val: &Option<Instant>, ser: S) -> Result<S::Ok, S::Error> {
|
||||
match val {
|
||||
Some(val) => {
|
||||
let now = chrono::Utc::now();
|
||||
let tsnow = Instant::now();
|
||||
let t1 = if tsnow >= *val {
|
||||
let dur = tsnow.duration_since(*val);
|
||||
let dur2 = chrono::Duration::seconds(dur.as_secs() as i64)
|
||||
.checked_add(&chrono::Duration::microseconds(dur.subsec_micros() as i64))
|
||||
.unwrap();
|
||||
now.checked_sub_signed(dur2).unwrap()
|
||||
} else {
|
||||
let dur = (*val).duration_since(tsnow);
|
||||
let dur2 = chrono::Duration::seconds(dur.as_secs() as i64)
|
||||
.checked_sub(&chrono::Duration::microseconds(dur.subsec_micros() as i64))
|
||||
.unwrap();
|
||||
now.checked_add_signed(dur2).unwrap()
|
||||
};
|
||||
//info!("formatting {:?}", t1);
|
||||
let s = t1.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||
//info!("final string {:?}", s);
|
||||
ser.serialize_str(&s)
|
||||
}
|
||||
None => ser.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ChannelError {
|
||||
#[allow(unused)]
|
||||
NoSuccess,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct EventedState {
|
||||
ts_last: Instant,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
enum MonitoringState {
|
||||
FetchSeriesId,
|
||||
AddingEvent(SeriesId),
|
||||
@@ -49,7 +92,7 @@ enum MonitoringState {
|
||||
Muted,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct CreatedState {
|
||||
#[allow(unused)]
|
||||
cid: u32,
|
||||
@@ -71,7 +114,7 @@ struct CreatedState {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
enum ChannelState {
|
||||
Init,
|
||||
Creating { cid: u32, ts_beg: Instant },
|
||||
@@ -79,6 +122,51 @@ enum ChannelState {
|
||||
Error(ChannelError),
|
||||
}
|
||||
|
||||
impl ChannelState {
|
||||
fn to_info(&self, name: String) -> ChannelStateInfo {
|
||||
let scalar_type = match self {
|
||||
ChannelState::Created(s) => Some(s.scalar_type.clone()),
|
||||
_ => None,
|
||||
};
|
||||
let shape = match self {
|
||||
ChannelState::Created(s) => Some(s.shape.clone()),
|
||||
_ => None,
|
||||
};
|
||||
let ts_created = match self {
|
||||
ChannelState::Created(s) => Some(s.ts_created.clone()),
|
||||
_ => None,
|
||||
};
|
||||
let ts_event_last = match self {
|
||||
ChannelState::Created(s) => match &s.state {
|
||||
MonitoringState::Evented(_, s) => Some(s.ts_last),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
let item_recv_ivl_ema = match self {
|
||||
ChannelState::Created(s) => {
|
||||
let ema = s.item_recv_ivl_ema.ema();
|
||||
if ema.update_count() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(ema.ema())
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let interest_score = 1. / item_recv_ivl_ema.unwrap_or(1e10).max(1e-6).min(1e10);
|
||||
ChannelStateInfo {
|
||||
name,
|
||||
scalar_type,
|
||||
shape,
|
||||
ts_created,
|
||||
ts_event_last,
|
||||
item_recv_ivl_ema,
|
||||
interest_score,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CaConnState {
|
||||
Unconnected,
|
||||
Connecting(Pin<Box<dyn Future<Output = Result<TcpStream, Error>> + Send>>),
|
||||
@@ -112,6 +200,10 @@ impl IdStore {
|
||||
#[derive(Debug)]
|
||||
pub enum ConnCommandKind {
|
||||
FindChannel(String, Sender<(SocketAddrV4, Vec<String>)>),
|
||||
ChannelState(String, Sender<(SocketAddrV4, Option<ChannelStateInfo>)>),
|
||||
ChannelStatesAll((), Sender<(SocketAddrV4, Vec<ChannelStateInfo>)>),
|
||||
ChannelAdd(String, Sender<bool>),
|
||||
ChannelRemove(String, Sender<bool>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -127,6 +219,46 @@ impl ConnCommand {
|
||||
};
|
||||
(cmd, rx)
|
||||
}
|
||||
|
||||
pub fn channel_state(
|
||||
name: String,
|
||||
) -> (
|
||||
ConnCommand,
|
||||
async_channel::Receiver<(SocketAddrV4, Option<ChannelStateInfo>)>,
|
||||
) {
|
||||
let (tx, rx) = async_channel::bounded(1);
|
||||
let cmd = Self {
|
||||
kind: ConnCommandKind::ChannelState(name, tx),
|
||||
};
|
||||
(cmd, rx)
|
||||
}
|
||||
|
||||
pub fn channel_states_all() -> (
|
||||
ConnCommand,
|
||||
async_channel::Receiver<(SocketAddrV4, Vec<ChannelStateInfo>)>,
|
||||
) {
|
||||
let (tx, rx) = async_channel::bounded(1);
|
||||
let cmd = Self {
|
||||
kind: ConnCommandKind::ChannelStatesAll((), tx),
|
||||
};
|
||||
(cmd, rx)
|
||||
}
|
||||
|
||||
pub fn channel_add(name: String) -> (ConnCommand, async_channel::Receiver<bool>) {
|
||||
let (tx, rx) = async_channel::bounded(1);
|
||||
let cmd = Self {
|
||||
kind: ConnCommandKind::ChannelAdd(name, tx),
|
||||
};
|
||||
(cmd, rx)
|
||||
}
|
||||
|
||||
pub fn channel_remove(name: String) -> (ConnCommand, async_channel::Receiver<bool>) {
|
||||
let (tx, rx) = async_channel::bounded(1);
|
||||
let cmd = Self {
|
||||
kind: ConnCommandKind::ChannelRemove(name, tx),
|
||||
};
|
||||
(cmd, rx)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@@ -136,6 +268,7 @@ pub struct CaConn {
|
||||
cid_store: IdStore,
|
||||
ioid_store: IdStore,
|
||||
subid_store: IdStore,
|
||||
// TODO use a Cid or so instead of u32.
|
||||
channels: BTreeMap<u32, ChannelState>,
|
||||
init_state_count: u64,
|
||||
cid_by_name: BTreeMap<String, u32>,
|
||||
@@ -210,7 +343,7 @@ impl CaConn {
|
||||
match self.conn_command_rx.poll_next_unpin(cx) {
|
||||
Ready(Some(a)) => match a.kind {
|
||||
ConnCommandKind::FindChannel(pattern, tx) => {
|
||||
info!("Search for {pattern:?}");
|
||||
//info!("Search for {pattern:?}");
|
||||
let mut res = Vec::new();
|
||||
for name in self.name_by_cid.values() {
|
||||
if !pattern.is_empty() && name.contains(&pattern) {
|
||||
@@ -225,6 +358,65 @@ impl CaConn {
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnCommandKind::ChannelState(name, tx) => {
|
||||
//info!("State for {name:?}");
|
||||
let res = match self.cid_by_name.get(&name) {
|
||||
Some(cid) => match self.channels.get(cid) {
|
||||
Some(state) => Some(state.to_info(name)),
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let msg = (self.remote_addr_dbg.clone(), res);
|
||||
if msg.1.is_some() {
|
||||
info!("Sending back {msg:?}");
|
||||
}
|
||||
match tx.try_send(msg) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
error!("response channel full or closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnCommandKind::ChannelStatesAll((), tx) => {
|
||||
let res = self
|
||||
.channels
|
||||
.iter()
|
||||
.map(|(cid, state)| {
|
||||
let name = self
|
||||
.name_by_cid
|
||||
.get(cid)
|
||||
.map_or("--unknown--".into(), |x| x.to_string());
|
||||
state.to_info(name)
|
||||
})
|
||||
.collect();
|
||||
let msg = (self.remote_addr_dbg.clone(), res);
|
||||
match tx.try_send(msg) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
error!("response channel full or closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnCommandKind::ChannelAdd(name, tx) => {
|
||||
self.channel_add(name);
|
||||
match tx.try_send(true) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
error!("response channel full or closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnCommandKind::ChannelRemove(name, tx) => {
|
||||
info!("remove {}", name);
|
||||
self.channel_remove(name);
|
||||
match tx.try_send(true) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
error!("response channel full or closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Ready(None) => {
|
||||
error!("Command queue closed");
|
||||
@@ -250,6 +442,13 @@ impl CaConn {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel_remove(&mut self, channel: String) {
|
||||
let cid = self.cid_by_name(&channel);
|
||||
if self.channels.contains_key(&cid) {
|
||||
warn!("TODO actually cause the channel to get closed and removed {}", channel);
|
||||
}
|
||||
}
|
||||
|
||||
fn cid_by_name(&mut self, name: &str) -> u32 {
|
||||
if let Some(cid) = self.cid_by_name.get(name) {
|
||||
*cid
|
||||
@@ -441,32 +640,25 @@ impl CaConn {
|
||||
fn handle_event_add_res(&mut self, ev: proto::EventAddRes, tsnow: Instant) -> Result<(), Error> {
|
||||
// TODO handle subid-not-found which can also be peer error:
|
||||
let cid = *self.cid_by_subid.get(&ev.subid).unwrap();
|
||||
//let name = self.name_by_cid(cid).unwrap().to_string();
|
||||
let _name = self.name_by_cid(cid).unwrap().to_string();
|
||||
// TODO get rid of the string clone when I don't want the log output any longer:
|
||||
// TODO handle not-found error:
|
||||
let mut series_2 = None;
|
||||
let ch_s = self.channels.get_mut(&cid).unwrap();
|
||||
match ch_s {
|
||||
ChannelState::Created(st) => {
|
||||
st.item_recv_ivl_ema.tick(Instant::now());
|
||||
st.item_recv_ivl_ema.tick(tsnow);
|
||||
let scalar_type = st.scalar_type.clone();
|
||||
let shape = st.shape.clone();
|
||||
match st.state {
|
||||
MonitoringState::AddingEvent(ref series) => {
|
||||
let series = series.clone();
|
||||
series_2 = Some(series.clone());
|
||||
// TODO get ts from faster common source:
|
||||
st.state = MonitoringState::Evented(
|
||||
series,
|
||||
EventedState {
|
||||
ts_last: Instant::now(),
|
||||
},
|
||||
);
|
||||
st.state = MonitoringState::Evented(series, EventedState { ts_last: tsnow });
|
||||
}
|
||||
MonitoringState::Evented(ref series, ref mut st) => {
|
||||
series_2 = Some(series.clone());
|
||||
// TODO get ts from faster common source:
|
||||
st.ts_last = Instant::now();
|
||||
st.ts_last = tsnow;
|
||||
}
|
||||
_ => {
|
||||
error!("unexpected state: EventAddRes while having {:?}", st.state);
|
||||
@@ -811,11 +1003,13 @@ impl Stream for CaConn {
|
||||
match tokio::time::timeout(Duration::from_millis(500), TcpStream::connect(addr)).await {
|
||||
Ok(Ok(k)) => Ok(k),
|
||||
Ok(Err(e)) => {
|
||||
error!("Can not connect to {addr:?} {e:?}");
|
||||
// TODO keep this in channel status field, or log when we have exponential backoff
|
||||
trace!("Can not connect to {addr:?} {e:?}");
|
||||
Err(e.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Can not connect to {addr:?} {e:?}");
|
||||
Err(_) => {
|
||||
// TODO keep this in channel status field, or log when we have exponential backoff
|
||||
trace!("Can not connect to {addr:?} timeout");
|
||||
Err(Error::with_msg_no_trace(format!("timeout")))
|
||||
}
|
||||
}
|
||||
@@ -832,7 +1026,8 @@ impl Stream for CaConn {
|
||||
continue 'outer;
|
||||
}
|
||||
Ready(Err(e)) => {
|
||||
error!("Connection error: {e:?}");
|
||||
// TODO keep this in channel status field, or log when we have exponential backoff
|
||||
trace!("Connection error: {e:?}");
|
||||
// We can not connect to the remote.
|
||||
// TODO do exponential backoff.
|
||||
self.state = CaConnState::Wait(wait_fut(10000));
|
||||
|
||||
@@ -601,7 +601,28 @@ impl CaMsg {
|
||||
let ca_secs = u32::from_be_bytes(payload[4..8].try_into()?);
|
||||
let ca_nanos = u32::from_be_bytes(payload[8..12].try_into()?);
|
||||
let ca_sh = Shape::from_ca_count(hi.data_count)?;
|
||||
let valbuf = &payload[12..];
|
||||
let meta_padding = match ca_dbr_ty.meta {
|
||||
CaDbrMetaType::Plain => 0,
|
||||
CaDbrMetaType::Status => match ca_dbr_ty.scalar_type {
|
||||
CaScalarType::I8 => 1,
|
||||
CaScalarType::I16 => 0,
|
||||
CaScalarType::I32 => 0,
|
||||
CaScalarType::F32 => 0,
|
||||
CaScalarType::F64 => 4,
|
||||
CaScalarType::Enum => 0,
|
||||
CaScalarType::String => 0,
|
||||
},
|
||||
CaDbrMetaType::Time => match ca_dbr_ty.scalar_type {
|
||||
CaScalarType::I8 => 3,
|
||||
CaScalarType::I16 => 2,
|
||||
CaScalarType::I32 => 0,
|
||||
CaScalarType::F32 => 0,
|
||||
CaScalarType::F64 => 4,
|
||||
CaScalarType::Enum => 2,
|
||||
CaScalarType::String => 0,
|
||||
},
|
||||
};
|
||||
let valbuf = &payload[12 + meta_padding..];
|
||||
let value = match ca_sh {
|
||||
Shape::Scalar => Self::ca_scalar_value(&ca_dbr_ty.scalar_type, valbuf)?,
|
||||
Shape::Wave(n) => {
|
||||
|
||||
+320
-24
@@ -1,22 +1,161 @@
|
||||
use crate::ca::conn::ConnCommand;
|
||||
use crate::ca::CommandQueueSet;
|
||||
use crate::ca::{CommandQueueSet, IngestCommons};
|
||||
use log::*;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct QueryForm {
|
||||
query: String,
|
||||
time: Option<f64>,
|
||||
#[allow(unused)]
|
||||
timeout: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PromLabels {
|
||||
start: Option<String>,
|
||||
end: Option<String>,
|
||||
//#[serde(rename = "match[]")]
|
||||
//pattern: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PromLabelValues {
|
||||
start: Option<String>,
|
||||
end: Option<String>,
|
||||
//#[serde(rename = "match[]")]
|
||||
//pattern: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
async fn get_empty() -> String {
|
||||
format!("")
|
||||
}
|
||||
|
||||
async fn channel_add(params: HashMap<String, String>, ingest_commons: Arc<IngestCommons>) -> String {
|
||||
if let (Some(backend), Some(name)) = (params.get("backend"), params.get("name")) {
|
||||
// TODO look up the address.
|
||||
match crate::ca::find_channel_addr(backend.into(), name.into(), &ingest_commons.pgconf).await {
|
||||
Ok(Some(addr)) => {
|
||||
if ingest_commons
|
||||
.command_queue_set
|
||||
.queues()
|
||||
.lock()
|
||||
.await
|
||||
.contains_key(&addr)
|
||||
{
|
||||
} else {
|
||||
match crate::ca::create_ca_conn(
|
||||
addr,
|
||||
ingest_commons.local_epics_hostname.clone(),
|
||||
256,
|
||||
32,
|
||||
ingest_commons.insert_item_queue.clone(),
|
||||
ingest_commons.data_store.clone(),
|
||||
ingest_commons.insert_ivl_min.clone(),
|
||||
ingest_commons.conn_stats.clone(),
|
||||
ingest_commons.command_queue_set.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
// TODO keep the join handle.
|
||||
}
|
||||
Err(_) => {
|
||||
error!("can not create CaConn");
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(tx) = ingest_commons.command_queue_set.queues().lock().await.get(&addr) {
|
||||
let (cmd, rx) = ConnCommand::channel_add(name.into());
|
||||
if let Err(_) = tx.send(cmd).await {
|
||||
error!("can not send command");
|
||||
"false".into()
|
||||
} else {
|
||||
match rx.recv().await {
|
||||
Ok(x) => {
|
||||
if x {
|
||||
"true".into()
|
||||
} else {
|
||||
"false".into()
|
||||
}
|
||||
}
|
||||
Err(_) => "false".into(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Even after create, can not locate the connection.");
|
||||
"false".into()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("can not find addr for channel");
|
||||
"false".into()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"false".into()
|
||||
}
|
||||
}
|
||||
|
||||
async fn channel_remove(
|
||||
params: HashMap<String, String>,
|
||||
ingest_commons: Arc<IngestCommons>,
|
||||
) -> axum::Json<serde_json::Value> {
|
||||
use axum::Json;
|
||||
use serde_json::Value;
|
||||
let addr = if let Some(x) = params.get("addr") {
|
||||
if let Ok(addr) = x.parse::<SocketAddrV4>() {
|
||||
addr
|
||||
} else {
|
||||
return Json(Value::Bool(false));
|
||||
}
|
||||
} else {
|
||||
return Json(Value::Bool(false));
|
||||
};
|
||||
let backend = if let Some(x) = params.get("backend") {
|
||||
x
|
||||
} else {
|
||||
return Json(Value::Bool(false));
|
||||
};
|
||||
let name = if let Some(x) = params.get("name") {
|
||||
x
|
||||
} else {
|
||||
return Json(Value::Bool(false));
|
||||
};
|
||||
if let Some(tx) = ingest_commons.command_queue_set.queues().lock().await.get(&addr) {
|
||||
// TODO any need to check the backend here?
|
||||
let _ = backend;
|
||||
let (cmd, rx) = ConnCommand::channel_remove(name.into());
|
||||
if let Err(_) = tx.send(cmd).await {
|
||||
error!("can not send command");
|
||||
Json(Value::Bool(false))
|
||||
} else {
|
||||
match rx.recv().await {
|
||||
Ok(x) => Json(Value::Bool(x)),
|
||||
Err(_) => Json(Value::Bool(false)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Json(Value::Bool(false))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_metrics_service(
|
||||
bind_to: String,
|
||||
insert_frac: Arc<AtomicU64>,
|
||||
insert_ivl_min: Arc<AtomicU64>,
|
||||
command_queue_set: Arc<CommandQueueSet>,
|
||||
ingest_commons: Arc<IngestCommons>,
|
||||
) {
|
||||
use axum::extract::Query;
|
||||
use axum::routing::{get, post, put};
|
||||
use axum::Form;
|
||||
use axum::{extract, Router};
|
||||
use http::request::Parts;
|
||||
let app = Router::new()
|
||||
@@ -38,26 +177,97 @@ pub async fn start_metrics_service(
|
||||
)
|
||||
.route(
|
||||
"/daqingest/find/channel",
|
||||
get(|Query(params): Query<HashMap<String, String>>| async move {
|
||||
let pattern = params.get("pattern").map_or(String::new(), |x| x.clone()).to_string();
|
||||
let g = command_queue_set.queues().lock().await;
|
||||
let mut rxs = Vec::new();
|
||||
for tx in g.iter() {
|
||||
let (cmd, rx) = ConnCommand::find_channel(pattern.clone());
|
||||
rxs.push(rx);
|
||||
if let Err(_) = tx.send(cmd).await {
|
||||
error!("can not send command");
|
||||
get({
|
||||
let command_queue_set = command_queue_set.clone();
|
||||
|Query(params): Query<HashMap<String, String>>| async move {
|
||||
let pattern = params.get("pattern").map_or(String::new(), |x| x.clone()).to_string();
|
||||
let g = command_queue_set.queues().lock().await;
|
||||
let mut rxs = Vec::new();
|
||||
for (_, tx) in g.iter() {
|
||||
let (cmd, rx) = ConnCommand::find_channel(pattern.clone());
|
||||
rxs.push(rx);
|
||||
if let Err(_) = tx.send(cmd).await {
|
||||
error!("can not send command");
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut res = Vec::new();
|
||||
for rx in rxs {
|
||||
let item = rx.recv().await.unwrap();
|
||||
let item = (item.0.to_string(), item.1);
|
||||
if item.1.len() > 0 {
|
||||
res.push(item);
|
||||
let mut res = Vec::new();
|
||||
for rx in rxs {
|
||||
let item = rx.recv().await.unwrap();
|
||||
if item.1.len() > 0 {
|
||||
let item = (item.0.to_string(), item.1);
|
||||
res.push(item);
|
||||
}
|
||||
}
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/daqingest/channel/state",
|
||||
get({
|
||||
let command_queue_set = command_queue_set.clone();
|
||||
|Query(params): Query<HashMap<String, String>>| async move {
|
||||
let name = params.get("name").map_or(String::new(), |x| x.clone()).to_string();
|
||||
let g = command_queue_set.queues().lock().await;
|
||||
let mut rxs = Vec::new();
|
||||
for (_, tx) in g.iter() {
|
||||
let (cmd, rx) = ConnCommand::channel_state(name.clone());
|
||||
rxs.push(rx);
|
||||
if let Err(_) = tx.send(cmd).await {
|
||||
error!("can not send command");
|
||||
}
|
||||
}
|
||||
let mut res = Vec::new();
|
||||
for rx in rxs {
|
||||
let item = rx.recv().await.unwrap();
|
||||
if let Some(st) = item.1 {
|
||||
let item = (item.0.to_string(), st);
|
||||
res.push(item);
|
||||
}
|
||||
}
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/daqingest/channel/states",
|
||||
get({
|
||||
let command_queue_set = command_queue_set.clone();
|
||||
|Query(_params): Query<HashMap<String, String>>| async move {
|
||||
let g = command_queue_set.queues().lock().await;
|
||||
let mut rxs = Vec::new();
|
||||
for (_, tx) in g.iter() {
|
||||
let (cmd, rx) = ConnCommand::channel_states_all();
|
||||
rxs.push(rx);
|
||||
if let Err(_) = tx.send(cmd).await {
|
||||
error!("can not send command");
|
||||
}
|
||||
}
|
||||
let mut res = Vec::new();
|
||||
for rx in rxs {
|
||||
let item = rx.recv().await.unwrap();
|
||||
for h in item.1 {
|
||||
res.push((item.0.clone(), h));
|
||||
}
|
||||
}
|
||||
res.sort_unstable_by_key(|(_, v)| v.interest_score as u32);
|
||||
let res: Vec<_> = res.into_iter().rev().take(10).collect();
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/daqingest/channel/add",
|
||||
get({
|
||||
let ingest_commons = ingest_commons.clone();
|
||||
|Query(params): Query<HashMap<String, String>>| async move { channel_add(params, ingest_commons).await }
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/daqingest/channel/remove",
|
||||
get({
|
||||
let ingest_commons = ingest_commons.clone();
|
||||
|Query(params): Query<HashMap<String, String>>| async move { channel_remove(params, ingest_commons).await }
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
@@ -79,11 +289,11 @@ pub async fn start_metrics_service(
|
||||
"status": "success",
|
||||
"data": {
|
||||
"version": "2.37",
|
||||
"revision": "aaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"revision": "daqingest",
|
||||
"branch": "dev",
|
||||
"buildUser": "empty",
|
||||
"buildDate": "2022-07-14",
|
||||
"goVersion": "go1"
|
||||
"buildUser": "dominik.werder",
|
||||
"buildDate": "2022-07-21",
|
||||
"goVersion": "nogo"
|
||||
}
|
||||
});
|
||||
serde_json::to_string(&res).unwrap()
|
||||
@@ -92,11 +302,97 @@ pub async fn start_metrics_service(
|
||||
.route(
|
||||
"/api/v1/query",
|
||||
post(
|
||||
|Query(params): Query<HashMap<String, String>>, parts: Parts| async move {
|
||||
info!("/api/v1/query params {params:?} {parts:?}");
|
||||
|Form(form): Form<QueryForm>, Query(params): Query<HashMap<String, String>>, parts: Parts| async move {
|
||||
info!("/api/v1/query form {form:?} params {params:?} {parts:?}");
|
||||
let res = if form.query == "1+1" {
|
||||
serde_json::json!({
|
||||
"status": "success",
|
||||
"data": {
|
||||
"resultType": "scalar",
|
||||
"result": [form.time.unwrap_or(0.0), "2"]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
serde_json::json!({
|
||||
"status": "success"
|
||||
})
|
||||
};
|
||||
serde_json::to_string(&res).unwrap()
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/labels",
|
||||
post(|Form(_form): Form<PromLabels>| async move {
|
||||
let res = {
|
||||
serde_json::json!({
|
||||
"status": "success",
|
||||
"data": ["__name__", "instance"]
|
||||
})
|
||||
};
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/label/__name__/values",
|
||||
get(|| async move {
|
||||
let res = {
|
||||
serde_json::json!({
|
||||
"status": "success",
|
||||
"data": ["series1", "series2"]
|
||||
})
|
||||
};
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/label/instance/values",
|
||||
get(|| async move {
|
||||
let res = {
|
||||
serde_json::json!({
|
||||
"status": "success",
|
||||
"data": ["node1", "node2"]
|
||||
})
|
||||
};
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/metadata",
|
||||
get(|| async move {
|
||||
let res = {
|
||||
serde_json::json!({
|
||||
"status": "success",
|
||||
"data": {}
|
||||
})
|
||||
};
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/series",
|
||||
post(|parts: Parts, body: bytes::Bytes| async move {
|
||||
info!("Asked for series, form: {parts:?}");
|
||||
let url = url::Url::parse(&format!("http://dummy{}", parts.uri))
|
||||
.unwrap_or_else(|_| url::Url::parse("http://a/").unwrap());
|
||||
info!("PARSED SERIES URL {:?}", url);
|
||||
let bodyparams = url::Url::parse(&String::from_utf8_lossy(&body));
|
||||
info!("BODY PARAMS: {:?}", bodyparams);
|
||||
let res = {
|
||||
serde_json::json!({
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"__name__": "series1",
|
||||
"job": "daqingest",
|
||||
"instance": "node1"
|
||||
}
|
||||
]
|
||||
})
|
||||
};
|
||||
serde_json::to_string(&res).unwrap()
|
||||
}),
|
||||
)
|
||||
.fallback(
|
||||
get(|parts: Parts, body: extract::RawBody<hyper::Body>| async move {
|
||||
let bytes = hyper::body::to_bytes(body.0).await.unwrap();
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::bsread::ChannelDescDecoded;
|
||||
use crate::errconv::ErrConv;
|
||||
use err::Error;
|
||||
use log::*;
|
||||
use serde::Serialize;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_postgres::Client as PgClient;
|
||||
|
||||
@@ -11,7 +12,7 @@ pub enum Existence<T> {
|
||||
Existing(T),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize)]
|
||||
pub struct SeriesId(u64);
|
||||
|
||||
impl SeriesId {
|
||||
|
||||
@@ -24,6 +24,15 @@ impl EMA {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_ema(ema: f32) -> Self {
|
||||
Self {
|
||||
ema,
|
||||
emv: 0.0,
|
||||
k: 0.05,
|
||||
update_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
ema: 0.0,
|
||||
|
||||
Reference in New Issue
Block a user