e5afd54ce0
* Basic storage stub * New models for new node status api * Route handling * Mounting new routes * Missing selective commit * Moved network monitor related files to separate package * Starting to see some sqlx action * Schema updates * Log statement upon finished migration * Removed old diesel related imports * Converted mixnode cache initialisation into a fairing * Moved cache related functionalities to separate package Also defined staging there * Created run method for validator cache + removed unwrap * Removed old node-status-api types and left bunch of todo placeholders in their place * Fixed managing validatorcache * Status reports are starting to get constructed * Submitting some dummy results to the database * Removing duplicate code for generating reports * Removed statuses older than 48h * Initial attempt at trying to obtain reports for all active nodes * Removed duplicates from the full report * Grabbing uptime history * Updating historical uptimes of active nodes * Updated sqlx-data.json * Removed all placeholder foomp owner values * Changed Layer serde behaviour for easier usage * Extended validator api config * Initial (seems working !) integration with network monitor * Added database path configuration to config * Using ValidatorCache in NetworkMonitor * Flag indicating whether validator cache has been initialised * Introduced a locla-only route for reward script to perform daily chores * Flag to save config to a file * Moved spawning of receiving future to run method rather than new * Removed arguments that dont make sense to be configured via CLI * Removed dead code from config file * More dead code removal * Added validator API to CI * Corrected manifest-path arguments * Constructing network monitor by passing config * Combined validator API CI with the main CI file * Using query_as for NodeStatus * Checking if historical uptimes were already calculated on particular day * Making id field NOT NULL * More query_as! action * Updated sqlx-data.json * Removed unused chrono feature * Renamed the migration file * Changed default validator endpoint to point to local validator * Removing unnecessary clone * More appropriate naming * Removed dead code * Lock file updates * Updated network monitor address in contract code * Don't stage node status api if network monitor is disabled * cargo fmt * Updated all license notices to SPDX
183 lines
6.1 KiB
Rust
183 lines
6.1 KiB
Rust
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crypto::asymmetric::identity;
|
|
use futures::stream::Stream;
|
|
use futures::task::Context;
|
|
use gateway_client::{AcknowledgementReceiver, MixnetMessageReceiver};
|
|
use std::pin::Pin;
|
|
use std::task::{Poll, Waker};
|
|
|
|
/// Constant used to determine maximum number of times the GatewayReader can poll. It basically
|
|
/// tries to solve the same problem that `FuturesUnordered` has: https://github.com/rust-lang/futures-rs/issues/2047
|
|
const YIELD_EVERY: usize = 32;
|
|
|
|
// TODO: Originally I set it to (identity::PublicKey, Vec<Vec<u8>>) and I definitely
|
|
// had a reason for doing so, but right now I can't remember what that was...
|
|
pub(crate) type GatewayMessages = Vec<Vec<u8>>;
|
|
|
|
pub(crate) struct GatewayChannel {
|
|
id: identity::PublicKey,
|
|
message_receiver: MixnetMessageReceiver,
|
|
ack_receiver: AcknowledgementReceiver,
|
|
is_closed: bool,
|
|
}
|
|
|
|
impl GatewayChannel {
|
|
pub(crate) fn new(
|
|
id: identity::PublicKey,
|
|
message_receiver: MixnetMessageReceiver,
|
|
ack_receiver: AcknowledgementReceiver,
|
|
) -> Self {
|
|
GatewayChannel {
|
|
id,
|
|
message_receiver,
|
|
ack_receiver,
|
|
is_closed: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Stream for GatewayChannel {
|
|
type Item = Vec<Vec<u8>>;
|
|
|
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
if self.is_closed {
|
|
return Poll::Ready(None);
|
|
}
|
|
|
|
let mut polled = 0;
|
|
|
|
// empty the ack channel if anything is on it (we don't care about the content at all at the
|
|
// moment)
|
|
while let Poll::Ready(_ack) = Pin::new(&mut self.ack_receiver).poll_next(cx) {
|
|
polled += 1;
|
|
}
|
|
|
|
let item = futures::ready!(Pin::new(&mut self.message_receiver).poll_next(cx));
|
|
|
|
match item {
|
|
None => Poll::Ready(None),
|
|
// if we managed to get an item, try to also read additional ones
|
|
Some(mut messages) => {
|
|
while let Poll::Ready(new_item) = Pin::new(&mut self.message_receiver).poll_next(cx)
|
|
{
|
|
polled += 1;
|
|
match new_item {
|
|
None => {
|
|
self.is_closed = true;
|
|
cx.waker().wake_by_ref();
|
|
return Poll::Ready(Some(messages));
|
|
}
|
|
Some(mut additional_messages) => {
|
|
messages.append(&mut additional_messages);
|
|
|
|
// it is fine enough to use the same constant here as in the main GatewayReader
|
|
// as only a single channel, i.e. the main gateway will be capable
|
|
// of returning more than 2 values
|
|
if polled == YIELD_EVERY {
|
|
cx.waker().wake_by_ref();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Poll::Ready(Some(messages))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) struct GatewaysReader {
|
|
latest_read: usize,
|
|
// channels: FuturesUnordered<GatewayChannel>,
|
|
channels: Vec<GatewayChannel>,
|
|
waker: Option<Waker>,
|
|
}
|
|
|
|
impl GatewaysReader {
|
|
pub(crate) fn new() -> Self {
|
|
GatewaysReader {
|
|
latest_read: 0,
|
|
channels: Vec::new(),
|
|
waker: None,
|
|
}
|
|
}
|
|
|
|
fn remove_nth(&mut self, i: usize) {
|
|
self.channels.remove(i);
|
|
}
|
|
|
|
// todo: if we find that this method is called frequently, perhaps the vector should get
|
|
// replaced with different data structure
|
|
pub(crate) fn remove_by_key(&mut self, key: identity::PublicKey) {
|
|
match self.channels.iter().position(|item| item.id == key) {
|
|
Some(i) => {
|
|
self.channels.remove(i);
|
|
}
|
|
// this shouldn't ever get thrown, so perhaps a panic would be more in order?
|
|
None => error!(
|
|
"tried to remove gateway reader {} but it doesn't exist!",
|
|
key.to_base58_string()
|
|
),
|
|
}
|
|
}
|
|
|
|
fn poll_nth(
|
|
&mut self,
|
|
cx: &mut Context<'_>,
|
|
i: usize,
|
|
) -> Option<Poll<Option<GatewayMessages>>> {
|
|
if let Poll::Ready(item) = Pin::new(&mut self.channels[i]).poll_next(cx) {
|
|
self.latest_read = i;
|
|
match item {
|
|
// Some(messages) => return Some(Poll::Ready(Some((self.channels[i].id, messages)))),
|
|
Some(messages) => return Some(Poll::Ready(Some(messages))),
|
|
// remove dead channel
|
|
None => self.remove_nth(i),
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub(crate) fn insert_channel(&mut self, channel: GatewayChannel) {
|
|
self.channels.push(channel);
|
|
if let Some(waker) = self.waker.take() {
|
|
waker.wake()
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: not sure if this will scale well, but I don't know what would be a good alternative,
|
|
// perhaps try to somehow incorporate FuturesUnordered?
|
|
// also, perhaps reading should be done in parallel?
|
|
impl Stream for GatewaysReader {
|
|
// item represents gateway that returned messages alongside the actual messages
|
|
type Item = GatewayMessages;
|
|
|
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
if self.latest_read >= self.channels.len() {
|
|
self.latest_read = 0;
|
|
}
|
|
|
|
// don't start reading from beginning each time to at least slightly help with the bias
|
|
for i in self.latest_read..self.channels.len() {
|
|
if let Some(item) = self.poll_nth(cx, i) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
for i in 0..self.latest_read {
|
|
if let Some(item) = self.poll_nth(cx, i) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
// if we have no channels available, store the waker to be woken when a new one is pushed
|
|
if self.channels.is_empty() {
|
|
self.waker = Some(cx.waker().clone())
|
|
}
|
|
Poll::Pending
|
|
}
|
|
}
|