WIP insert
This commit is contained in:
@@ -1,15 +1,22 @@
|
||||
use super::proto::{CaItem, CaMsg, CaMsgTy, CaProto};
|
||||
use super::store::DataStore;
|
||||
use crate::bsread::ChannelDescDecoded;
|
||||
use crate::ca::proto::{CreateChan, EventAdd, HeadInfo, ReadNotify};
|
||||
use crate::series::{Existence, SeriesId};
|
||||
use err::Error;
|
||||
use futures_util::{Future, FutureExt, Stream, StreamExt};
|
||||
use futures_util::stream::FuturesUnordered;
|
||||
use futures_util::{Future, FutureExt, Stream, StreamExt, TryFutureExt};
|
||||
use libc::c_int;
|
||||
use log::*;
|
||||
use netpod::timeunits::SEC;
|
||||
use netpod::{ScalarType, Shape};
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
@@ -25,8 +32,10 @@ struct EventedState {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MonitoringState {
|
||||
AddingEvent,
|
||||
Evented(EventedState),
|
||||
FetchSeriesId,
|
||||
AddingEvent(SeriesId),
|
||||
Evented(SeriesId, EventedState),
|
||||
// TODO we also want to read while staying subscribed:
|
||||
Reading,
|
||||
Read,
|
||||
Muted,
|
||||
@@ -36,6 +45,8 @@ enum MonitoringState {
|
||||
struct CreatedState {
|
||||
cid: u32,
|
||||
sid: u32,
|
||||
scalar_type: ScalarType,
|
||||
shape: Shape,
|
||||
ts_created: Instant,
|
||||
state: MonitoringState,
|
||||
}
|
||||
@@ -82,10 +93,15 @@ pub struct CaConn {
|
||||
cid_by_subid: BTreeMap<u32, u32>,
|
||||
name_by_cid: BTreeMap<u32, String>,
|
||||
poll_count: usize,
|
||||
data_store: Arc<DataStore>,
|
||||
fut_get_series: FuturesUnordered<
|
||||
Pin<Box<dyn Future<Output = Result<(u32, u32, u16, u16, Existence<SeriesId>), Error>> + Send>>,
|
||||
>,
|
||||
value_insert_futs: FuturesUnordered<Pin<Box<dyn Future<Output = Result<(), Error>> + Send>>>,
|
||||
}
|
||||
|
||||
impl CaConn {
|
||||
pub fn new(tcp: TcpStream) -> Self {
|
||||
pub fn new(tcp: TcpStream, data_store: Arc<DataStore>) -> Self {
|
||||
Self {
|
||||
state: CaConnState::Init,
|
||||
proto: CaProto::new(tcp),
|
||||
@@ -97,6 +113,9 @@ impl CaConn {
|
||||
cid_by_subid: BTreeMap::new(),
|
||||
name_by_cid: BTreeMap::new(),
|
||||
poll_count: 0,
|
||||
data_store,
|
||||
fut_get_series: FuturesUnordered::new(),
|
||||
value_insert_futs: FuturesUnordered::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +154,65 @@ impl Stream for CaConn {
|
||||
return Ready(None);
|
||||
}
|
||||
loop {
|
||||
while self.value_insert_futs.len() > 0 {
|
||||
match self.fut_get_series.poll_next_unpin(cx) {
|
||||
Pending => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
while self.fut_get_series.len() > 0 {
|
||||
match self.fut_get_series.poll_next_unpin(cx) {
|
||||
Ready(Some(Ok(k))) => {
|
||||
info!("Have SeriesId {k:?}");
|
||||
let cid = k.0;
|
||||
let sid = k.1;
|
||||
let data_type = k.2;
|
||||
let data_count = k.3;
|
||||
let series = match k.4 {
|
||||
Existence::Created(k) => k,
|
||||
Existence::Existing(k) => k,
|
||||
};
|
||||
let subid = self.subid_store.next();
|
||||
self.cid_by_subid.insert(subid, cid);
|
||||
let name = self.name_by_cid(cid).unwrap().to_string();
|
||||
let msg = CaMsg {
|
||||
ty: CaMsgTy::EventAdd(EventAdd {
|
||||
sid,
|
||||
data_type,
|
||||
data_count,
|
||||
subid,
|
||||
}),
|
||||
};
|
||||
self.proto.push_out(msg);
|
||||
// TODO handle not-found error:
|
||||
let ch_s = self.channels.get_mut(&cid).unwrap();
|
||||
*ch_s = ChannelState::Created(CreatedState {
|
||||
cid,
|
||||
sid,
|
||||
// TODO handle error better! Transition channel to Error state?
|
||||
scalar_type: ScalarType::from_ca_id(data_type)?,
|
||||
shape: Shape::from_ca_count(data_count)?,
|
||||
ts_created: Instant::now(),
|
||||
state: MonitoringState::AddingEvent(series),
|
||||
});
|
||||
let scalar_type = ScalarType::from_ca_id(data_type)?;
|
||||
let shape = Shape::from_ca_count(data_count)?;
|
||||
let _cd = ChannelDescDecoded {
|
||||
name: name.to_string(),
|
||||
scalar_type,
|
||||
shape,
|
||||
agg_kind: netpod::AggKind::Plain,
|
||||
// TODO these play no role in series id:
|
||||
byte_order: netpod::ByteOrder::LE,
|
||||
compression: None,
|
||||
};
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
Ready(Some(Err(e))) => error!("series error: {e:?}"),
|
||||
Ready(None) => {}
|
||||
Pending => break,
|
||||
}
|
||||
}
|
||||
break match &self.state {
|
||||
CaConnState::Init => {
|
||||
let msg = CaMsg { ty: CaMsgTy::Version };
|
||||
@@ -229,75 +307,169 @@ impl Stream for CaConn {
|
||||
let res = match self.proto.poll_next_unpin(cx) {
|
||||
Ready(Some(Ok(k))) => {
|
||||
match k {
|
||||
CaItem::Msg(k) => match k.ty {
|
||||
CaMsgTy::SearchRes(k) => {
|
||||
let a = k.addr.to_be_bytes();
|
||||
let addr = format!("{}.{}.{}.{}:{}", a[0], a[1], a[2], a[3], k.tcp_port);
|
||||
info!("Search result indicates server address: {addr}");
|
||||
}
|
||||
CaMsgTy::CreateChanRes(k) => {
|
||||
// TODO handle cid-not-found which can also indicate peer error.
|
||||
let cid = k.cid;
|
||||
let name = self.name_by_cid(cid);
|
||||
info!("Channel created for {name:?} now register for events");
|
||||
let subid = self.subid_store.next();
|
||||
self.cid_by_subid.insert(subid, cid);
|
||||
let msg = CaMsg {
|
||||
ty: CaMsgTy::EventAdd(EventAdd {
|
||||
sid: k.sid,
|
||||
data_type: k.data_type,
|
||||
data_count: k.data_count,
|
||||
subid,
|
||||
}),
|
||||
};
|
||||
self.proto.push_out(msg);
|
||||
do_wake_again = true;
|
||||
// TODO handle not-found error:
|
||||
let ch_s = self.channels.get_mut(&k.cid).unwrap();
|
||||
*ch_s = ChannelState::Created(CreatedState {
|
||||
cid: k.cid,
|
||||
sid: k.sid,
|
||||
ts_created: Instant::now(),
|
||||
state: MonitoringState::AddingEvent,
|
||||
});
|
||||
info!(
|
||||
"Channel is created cid {} sid {} name {}",
|
||||
k.cid, k.sid, self.name_by_cid[&k.cid]
|
||||
);
|
||||
}
|
||||
CaMsgTy::EventAddRes(k) => {
|
||||
// TODO handle subid-not-found which can also be peer error:
|
||||
let cid = *self.cid_by_subid.get(&k.subid).unwrap();
|
||||
// TODO get rid of the string clone when I don't want the log output any longer:
|
||||
let name: String = self.name_by_cid(cid).unwrap().into();
|
||||
// TODO handle not-found error:
|
||||
let ch_s = self.channels.get_mut(&cid).unwrap();
|
||||
match ch_s {
|
||||
ChannelState::Created(st) => {
|
||||
match st.state {
|
||||
MonitoringState::AddingEvent => {
|
||||
info!("Confirmation {name} is subscribed.");
|
||||
// TODO get ts from faster common source:
|
||||
st.state = MonitoringState::Evented(EventedState {
|
||||
ts_last: Instant::now(),
|
||||
});
|
||||
}
|
||||
MonitoringState::Evented(ref mut st) => {
|
||||
// TODO get ts from faster common source:
|
||||
st.ts_last = Instant::now();
|
||||
}
|
||||
_ => {
|
||||
warn!("bad state? not always, could be late message.");
|
||||
CaItem::Msg(k) => {
|
||||
match k.ty {
|
||||
CaMsgTy::SearchRes(k) => {
|
||||
let a = k.addr.to_be_bytes();
|
||||
let addr = format!("{}.{}.{}.{}:{}", a[0], a[1], a[2], a[3], k.tcp_port);
|
||||
info!("Search result indicates server address: {addr}");
|
||||
}
|
||||
CaMsgTy::CreateChanRes(k) => {
|
||||
// TODO handle cid-not-found which can also indicate peer error.
|
||||
let cid = k.cid;
|
||||
let sid = k.sid;
|
||||
// TODO handle error:
|
||||
let name = self.name_by_cid(cid).unwrap().to_string();
|
||||
info!("CreateChanRes {name:?}");
|
||||
let scalar_type = ScalarType::from_ca_id(k.data_type)?;
|
||||
let shape = Shape::from_ca_count(k.data_count)?;
|
||||
// TODO handle not-found error:
|
||||
let ch_s = self.channels.get_mut(&cid).unwrap();
|
||||
*ch_s = ChannelState::Created(CreatedState {
|
||||
cid,
|
||||
sid,
|
||||
scalar_type: scalar_type.clone(),
|
||||
shape: shape.clone(),
|
||||
ts_created: Instant::now(),
|
||||
state: MonitoringState::FetchSeriesId,
|
||||
});
|
||||
// TODO handle error in different way. Should most likely not abort.
|
||||
let cd = ChannelDescDecoded {
|
||||
name: name.to_string(),
|
||||
scalar_type,
|
||||
shape,
|
||||
agg_kind: netpod::AggKind::Plain,
|
||||
// TODO these play no role in series id:
|
||||
byte_order: netpod::ByteOrder::LE,
|
||||
compression: None,
|
||||
};
|
||||
let y = self.as_ref();
|
||||
let y = unsafe { Pin::into_inner_unchecked(y) };
|
||||
let y = unsafe { &*(y as *const CaConn) };
|
||||
let fut =
|
||||
y.data_store.chan_reg.get_series_id(cd).map_ok(move |series| {
|
||||
(cid, k.sid, k.data_type, k.data_count, series)
|
||||
});
|
||||
// TODO throttle execution rate:
|
||||
self.fut_get_series.push(Box::pin(fut) as _);
|
||||
do_wake_again = true;
|
||||
}
|
||||
CaMsgTy::EventAddRes(k) => {
|
||||
// TODO handle subid-not-found which can also be peer error:
|
||||
let cid = *self.cid_by_subid.get(&k.subid).unwrap();
|
||||
// TODO get rid of the string clone when I don't want the log output any longer:
|
||||
let name: String = self.name_by_cid(cid).unwrap().into();
|
||||
// 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) => {
|
||||
match st.state {
|
||||
MonitoringState::AddingEvent(ref series) => {
|
||||
let series = series.clone();
|
||||
series_2 = Some(series.clone());
|
||||
info!("Confirmation {name} is subscribed.");
|
||||
// TODO get ts from faster common source:
|
||||
st.state = MonitoringState::Evented(
|
||||
series,
|
||||
EventedState {
|
||||
ts_last: Instant::now(),
|
||||
},
|
||||
);
|
||||
}
|
||||
MonitoringState::Evented(ref series, ref mut st) => {
|
||||
series_2 = Some(series.clone());
|
||||
// TODO get ts from faster common source:
|
||||
st.ts_last = Instant::now();
|
||||
}
|
||||
_ => {
|
||||
error!(
|
||||
"unexpected state: EventAddRes while having {ch_s:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("unexpected state: EventAddRes while having {ch_s:?}");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("unexpected state: EventAddRes while having {ch_s:?}");
|
||||
{
|
||||
let series = series_2.unwrap();
|
||||
// TODO where to actually get the time from?
|
||||
let ts = SystemTime::now();
|
||||
let epoch = ts.duration_since(std::time::UNIX_EPOCH).unwrap();
|
||||
let ts = epoch.as_secs() * 1000000000 + epoch.subsec_nanos() as u64;
|
||||
let ts_msp = ts / (30 * SEC) * (30 * SEC);
|
||||
let ts_lsp = ts - ts_msp;
|
||||
// TODO make sure that I only accept types I expect.
|
||||
use crate::ca::proto::CaDataScalarValue::*;
|
||||
use crate::ca::proto::CaDataValue::*;
|
||||
match k.value {
|
||||
Scalar(v) => {
|
||||
match v {
|
||||
F64(val) => {
|
||||
match ch_s {
|
||||
ChannelState::Created(st) => {
|
||||
match st.shape {
|
||||
Shape::Scalar => {
|
||||
match st.scalar_type {
|
||||
ScalarType::F64 => {
|
||||
// TODO get msp/lsp: cap at some count. only one writer!!
|
||||
let y = self.as_ref();
|
||||
let y = unsafe {
|
||||
Pin::into_inner_unchecked(y)
|
||||
};
|
||||
let y = unsafe {
|
||||
&*(y as *const CaConn)
|
||||
};
|
||||
let fut1 = y.data_store.scy.execute(
|
||||
&y.data_store.qu_insert_ts_msp,
|
||||
(ts_msp as i64, ts_lsp as i64),
|
||||
).map(|_| Ok::<_, Error>(()))
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("{e:?}")));
|
||||
let fut2 = y.data_store.scy.execute(
|
||||
&y.data_store.qu_insert_scalar_f64,
|
||||
(series.id() as i64, ts_msp as i64, ts_lsp as i64, 0i64, val),
|
||||
).map(|_| Ok::<_, Error>(()))
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("{e:?}")));
|
||||
let fut = fut1
|
||||
.and_then(move |a| fut2);
|
||||
if self.value_insert_futs.len()
|
||||
> 10
|
||||
{
|
||||
warn!("can not keep up");
|
||||
} else {
|
||||
self.value_insert_futs
|
||||
.push(
|
||||
Box::pin(fut) as _
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("unexpected value type");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("unexpected value shape");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("got value but channel not created");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Array => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ready(Some(Ok(())))
|
||||
|
||||
@@ -63,6 +63,7 @@ pub struct EventAddRes {
|
||||
pub data_count: u16,
|
||||
pub status: u32,
|
||||
pub subid: u32,
|
||||
pub value: CaDataValue,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -92,6 +93,23 @@ enum CaScalarType {
|
||||
String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CaDataScalarValue {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
Enum(i16),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CaDataValue {
|
||||
Scalar(CaDataScalarValue),
|
||||
Array,
|
||||
}
|
||||
|
||||
impl CaScalarType {
|
||||
fn from_ca_u16(k: u16) -> Result<Self, Error> {
|
||||
use CaScalarType::*;
|
||||
@@ -416,7 +434,7 @@ impl CaMsg {
|
||||
}
|
||||
1 => {
|
||||
let ca_st = CaScalarType::from_ca_u16(hi.data_type)?;
|
||||
match ca_st {
|
||||
let value = match ca_st {
|
||||
CaScalarType::F64 => {
|
||||
if payload.len() < 2 {
|
||||
return Err(Error::with_msg_no_trace(format!(
|
||||
@@ -425,7 +443,7 @@ impl CaMsg {
|
||||
)));
|
||||
}
|
||||
let v = f64::from_be_bytes(payload.try_into()?);
|
||||
info!("f64: {v}");
|
||||
CaDataValue::Scalar(CaDataScalarValue::F64(v))
|
||||
}
|
||||
CaScalarType::Enum => {
|
||||
if payload.len() < 2 {
|
||||
@@ -434,8 +452,8 @@ impl CaMsg {
|
||||
payload.len()
|
||||
)));
|
||||
}
|
||||
let v = u16::from_be_bytes(payload[..2].try_into()?);
|
||||
info!("enum payload: {v}");
|
||||
let v = i16::from_be_bytes(payload[..2].try_into()?);
|
||||
CaDataValue::Scalar(CaDataScalarValue::I16(v))
|
||||
}
|
||||
CaScalarType::String => {
|
||||
let mut ixn = payload.len();
|
||||
@@ -447,17 +465,19 @@ impl CaMsg {
|
||||
}
|
||||
//info!("try to read string from payload len {} ixn {}", payload.len(), ixn);
|
||||
let v = String::from_utf8_lossy(&payload[..ixn]);
|
||||
info!("String payload: {v}");
|
||||
CaDataValue::Scalar(CaDataScalarValue::String(v.into()))
|
||||
}
|
||||
_ => {
|
||||
warn!("TODO handle {ca_st:?}");
|
||||
return Err(Error::with_msg_no_trace(format!("can not yet handle type {ca_st:?}")));
|
||||
}
|
||||
}
|
||||
};
|
||||
let d = EventAddRes {
|
||||
data_type: hi.data_type,
|
||||
data_count: hi.data_count,
|
||||
status: hi.param1,
|
||||
subid: hi.param2,
|
||||
value,
|
||||
};
|
||||
CaMsg {
|
||||
ty: CaMsgTy::EventAddRes(d),
|
||||
|
||||
65
netfetch/src/ca/store.rs
Normal file
65
netfetch/src/ca/store.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use crate::bsread::ChannelDescDecoded;
|
||||
use crate::series::{Existence, SeriesId};
|
||||
use async_channel::{Receiver, Sender};
|
||||
use err::Error;
|
||||
use scylla::prepared_statement::PreparedStatement;
|
||||
use scylla::Session as ScySession;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct RegisterJob {
|
||||
desc: ChannelDescDecoded,
|
||||
}
|
||||
|
||||
impl RegisterJob {
|
||||
pub fn new(desc: ChannelDescDecoded) -> Self {
|
||||
Self { desc }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegisterChannel {
|
||||
tx: Sender<RegisterJob>,
|
||||
rx: Receiver<RegisterJob>,
|
||||
}
|
||||
|
||||
pub struct ChannelRegistry {
|
||||
scy: Arc<ScySession>,
|
||||
}
|
||||
|
||||
impl ChannelRegistry {
|
||||
pub fn new(scy: Arc<ScySession>) -> Self {
|
||||
Self { scy }
|
||||
}
|
||||
|
||||
pub async fn get_series_id(&self, cd: ChannelDescDecoded) -> Result<Existence<SeriesId>, Error> {
|
||||
crate::series::get_series_id(&self.scy, &cd).await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataStore {
|
||||
pub scy: Arc<ScySession>,
|
||||
pub qu_insert_ts_msp: Arc<PreparedStatement>,
|
||||
pub qu_insert_scalar_f64: Arc<PreparedStatement>,
|
||||
pub chan_reg: Arc<ChannelRegistry>,
|
||||
}
|
||||
|
||||
impl DataStore {
|
||||
pub async fn new(scy: Arc<ScySession>) -> Result<Self, Error> {
|
||||
let q = scy
|
||||
.prepare("insert into ts_msp (series, ts_msp) values (?, ?)")
|
||||
.await
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("{e:?}")))?;
|
||||
let qu_insert_ts_msp = Arc::new(q);
|
||||
let q = scy
|
||||
.prepare("insert into events_scalar_f64 (series, ts_msp, ts_lsp, pulse, value) values (?, ?, ?, ?, ?)")
|
||||
.await
|
||||
.map_err(|e| Error::with_msg_no_trace(format!("{e:?}")))?;
|
||||
let qu_insert_scalar_f64 = Arc::new(q);
|
||||
let ret = Self {
|
||||
chan_reg: Arc::new(ChannelRegistry::new(scy.clone())),
|
||||
scy,
|
||||
qu_insert_ts_msp,
|
||||
qu_insert_scalar_f64,
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user