mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-06-12 20:21:01 +00:00
Rust backend (#185)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
pub type Id = usize;
|
||||
|
||||
pub struct DenseMap<T> {
|
||||
/// List of retired indexes that can be re-used
|
||||
retired: Vec<Id>,
|
||||
/// All items
|
||||
items: Vec<Option<T>>,
|
||||
}
|
||||
|
||||
impl<T> DenseMap<T> {
|
||||
pub fn new() -> Self {
|
||||
DenseMap {
|
||||
retired: Vec::new(),
|
||||
items: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, item: T) -> Id {
|
||||
self.add_with(|_| item)
|
||||
}
|
||||
|
||||
pub fn add_with<F>(&mut self, f: F) -> Id
|
||||
where
|
||||
F: FnOnce(Id) -> T,
|
||||
{
|
||||
match self.retired.pop() {
|
||||
Some(id) => {
|
||||
self.items[id] = Some(f(id));
|
||||
id
|
||||
},
|
||||
None => {
|
||||
let id = self.items.len();
|
||||
self.items.push(Some(f(id)));
|
||||
id
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, id: Id) -> Option<&T> {
|
||||
self.items.get(id).and_then(|item| item.as_ref())
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
|
||||
self.items.get_mut(id).and_then(|item| item.as_mut())
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: Id) -> Option<T> {
|
||||
let old = self.items.get_mut(id).and_then(|item| item.take());
|
||||
|
||||
if old.is_some() {
|
||||
// something was actually removed, so lets add the id to
|
||||
// the list of retired ids!
|
||||
self.retired.push(id);
|
||||
}
|
||||
|
||||
old
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Id, &T)> + '_ {
|
||||
self.items.iter().enumerate().filter_map(|(id, item)| {
|
||||
Some((id, item.as_ref()?))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Id, &mut T)> + '_ {
|
||||
self.items.iter_mut().enumerate().filter_map(|(id, item)| {
|
||||
Some((id, item.as_mut()?))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len() - self.retired.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::chain::{Chain, LocateNode};
|
||||
use crate::types::{NodeId, NodeLocation};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Locator {
|
||||
client: reqwest::Client,
|
||||
cache: Arc<RwLock<FxHashMap<Ipv4Addr, Option<Arc<NodeLocation>>>>>,
|
||||
}
|
||||
|
||||
pub struct LocatorFactory {
|
||||
cache: Arc<RwLock<FxHashMap<Ipv4Addr, Option<Arc<NodeLocation>>>>>,
|
||||
}
|
||||
|
||||
impl LocatorFactory {
|
||||
pub fn new() -> Self {
|
||||
let mut cache = FxHashMap::default();
|
||||
|
||||
// Default entry for localhost
|
||||
cache.insert(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
Some(Arc::new(NodeLocation { latitude: 52.5166667, longitude: 13.4, city: "Berlin".into() })),
|
||||
);
|
||||
|
||||
LocatorFactory {
|
||||
cache: Arc::new(RwLock::new(cache)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(&self) -> Locator {
|
||||
Locator {
|
||||
client: reqwest::Client::new(),
|
||||
cache: self.cache.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for Locator {
|
||||
type Context = SyncContext<Self>;
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
pub struct LocateRequest {
|
||||
pub ip: Ipv4Addr,
|
||||
pub nid: NodeId,
|
||||
pub chain: Addr<Chain>,
|
||||
}
|
||||
|
||||
impl Handler<LocateRequest> for Locator {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: LocateRequest, _: &mut Self::Context) {
|
||||
let LocateRequest { ip, nid, chain } = msg;
|
||||
|
||||
if let Some(item) = self.cache.read().get(&ip) {
|
||||
if let Some(location) = item {
|
||||
return chain.do_send(LocateNode { nid, location: location.clone() });
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let ip_req = format!("https://ipapi.co/{}/json", ip);
|
||||
let mut response = match self.client.post(&ip_req).send() {
|
||||
Ok(response) => response,
|
||||
Err(err) => return debug!("POST error for ip location: {:?}", err),
|
||||
};
|
||||
|
||||
let location = match response.json::<NodeLocation>() {
|
||||
Ok(location) => Some(Arc::new(location)),
|
||||
Err(err) => {
|
||||
debug!("JSON error for ip location: {:?}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
self.cache.write().insert(ip, location.clone());
|
||||
|
||||
if let Some(location) = location {
|
||||
chain.do_send(LocateNode { nid, location });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use num_traits::{Float, Zero};
|
||||
use std::ops::AddAssign;
|
||||
|
||||
pub struct MeanList<T> where T: Float + AddAssign + Zero + From<u8> {
|
||||
period_sum: T,
|
||||
period_count: u8,
|
||||
mean_index: u8,
|
||||
means: [T; 20],
|
||||
ticks_per_mean: u8,
|
||||
}
|
||||
|
||||
impl<T> MeanList<T> where T: Float + AddAssign + Zero + From<u8> {
|
||||
pub fn new() -> MeanList<T> {
|
||||
MeanList {
|
||||
period_sum: T::zero(),
|
||||
period_count: 0,
|
||||
mean_index: 0,
|
||||
means: [T::zero(); 20],
|
||||
ticks_per_mean: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slice(&self) -> &[T] {
|
||||
&self.means[..usize::from(self.mean_index)]
|
||||
}
|
||||
|
||||
pub fn push(&mut self, val: T) -> bool {
|
||||
if self.mean_index == 20 && self.ticks_per_mean < 32 {
|
||||
self.squash_means();
|
||||
}
|
||||
|
||||
self.period_sum += val;
|
||||
self.period_count += 1;
|
||||
|
||||
if self.period_count == self.ticks_per_mean {
|
||||
self.push_mean();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn push_mean(&mut self) {
|
||||
let mean = self.period_sum / std::convert::From::from(self.period_count);
|
||||
|
||||
if self.mean_index == 20 && self.ticks_per_mean == 32 {
|
||||
self.means.rotate_left(1);
|
||||
self.means[19] = mean;
|
||||
} else {
|
||||
self.means[usize::from(self.mean_index)] = mean;
|
||||
self.mean_index += 1;
|
||||
}
|
||||
|
||||
self.period_sum = T::zero();
|
||||
self.period_count = 0;
|
||||
}
|
||||
|
||||
fn squash_means(&mut self) {
|
||||
self.ticks_per_mean *= 2;
|
||||
self.mean_index = 10;
|
||||
|
||||
for i in 0..10 {
|
||||
let i2 = i * 2;
|
||||
|
||||
self.means[i] = (self.means[i2] + self.means[i2 + 1]) / std::convert::From::from(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use num_traits::{Zero, NumOps, Bounded};
|
||||
use std::iter::Sum;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Keep track of last N numbers pushed onto internal stack.
|
||||
/// Provides means to get an average of said numbers.
|
||||
pub struct NumStats<T> {
|
||||
stack: Box<[T]>,
|
||||
index: usize,
|
||||
sum: T,
|
||||
}
|
||||
|
||||
impl<T: NumOps + Zero + Bounded + Copy + Sum + TryFrom<usize>> NumStats<T> {
|
||||
pub fn new(size: usize) -> Self {
|
||||
NumStats {
|
||||
stack: vec![T::zero(); size].into_boxed_slice(),
|
||||
index: 0,
|
||||
sum: T::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, val: T) {
|
||||
let slot = &mut self.stack[self.index % self.stack.len()];
|
||||
|
||||
self.sum = self.sum - *slot + val;
|
||||
|
||||
*slot = val;
|
||||
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
pub fn average(&self) -> T {
|
||||
let cap = std::cmp::min(self.index, self.stack.len());
|
||||
let cap = T::try_from(cap).unwrap_or_else(|_| T::max_value());
|
||||
|
||||
self.sum / cap
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.index = 0;
|
||||
self.sum = T::zero();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user