latency tester

This commit is contained in:
GroovieGermanikus 2024-07-25 13:28:50 +02:00
parent 6c1fc7df59
commit c91d434881
No known key found for this signature in database
GPG Key ID: 5B6EB831A5CD2015
2 changed files with 71 additions and 5 deletions

View File

@ -52,3 +52,18 @@ cargo run --bin rpc-node-check-alive
``` ```
![example discord message](discord1.png) ![example discord message](discord1.png)
## Slot Latency
```
279582120(TritonRpc) .......... 279582130(SolanaWebsocket) . 279582131(SolanaRpc) .. 279582133(YellowstoneGrpc) // no data from [TritonWebsocket]
279582120(TritonRpc) ........... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) .. 279582133(YellowstoneGrpc) // no data from [TritonWebsocket]
279582120(TritonRpc) ........... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) .. 279582133(YellowstoneGrpc) // no data from [TritonWebsocket]
279582120(TritonRpc) ........... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) .. 279582133(YellowstoneGrpc) // no data from [TritonWebsocket]
279582120(TritonRpc) .. 279582122(TritonWebsocket) ......... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) .. 279582133(YellowstoneGrpc) // all sources have data
279582120(TritonRpc) .. 279582122(TritonWebsocket) ......... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) .. 279582133(YellowstoneGrpc) // all sources have data
279582120(TritonRpc) .. 279582122(TritonWebsocket) ......... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) ... 279582134(YellowstoneGrpc) // all sources have data
279582121(TritonRpc) . 279582122(TritonWebsocket) ......... 279582131(SolanaWebsocket) = 279582131(SolanaRpc) ... 279582134(YellowstoneGrpc) // all sources have data
```

View File

@ -7,11 +7,13 @@ use solana_rpc_client_api::request::TokenAccountsFilter;
use solana_rpc_client_api::response::SlotInfo; use solana_rpc_client_api::response::SlotInfo;
use solana_sdk::commitment_config::CommitmentConfig; use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::pin::pin; use std::pin::pin;
use std::str::FromStr; use std::str::FromStr;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use enum_iterator::Sequence;
use itertools::Itertools;
use tokio::select; use tokio::select;
use tokio::sync::mpsc::error::SendError; use tokio::sync::mpsc::error::SendError;
use tokio::time::Instant; use tokio::time::Instant;
@ -26,7 +28,7 @@ use yellowstone_grpc_proto::geyser::{
type Slot = u64; type Slot = u64;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Eq, Hash, PartialEq, Sequence)]
enum SlotSource { enum SlotSource {
SolanaWebsocket, SolanaWebsocket,
SolanaRpc, SolanaRpc,
@ -100,15 +102,18 @@ async fn main() {
tokio::spawn(rpc_getslot_source(triton_rpc_url, SlotSource::TritonRpc, slots_tx.clone())); tokio::spawn(rpc_getslot_source(triton_rpc_url, SlotSource::TritonRpc, slots_tx.clone()));
let started_at = Instant::now(); let started_at = Instant::now();
let mut latest_slot_per_source: HashMap<SlotSource, Slot> = HashMap::new();
while let Some(SlotDatapoint { slot, source, .. }) = slots_rx.recv().await { while let Some(SlotDatapoint { slot, source, .. }) = slots_rx.recv().await {
println!("Slot from {:?}: {}", source, slot); // println!("Slot from {:?}: {}", source, slot);
latest_slot_per_source.insert(source, slot);
if Instant::now().duration_since(started_at) > Duration::from_secs(2) { visualize_slots(&latest_slot_per_source).await;
if Instant::now().duration_since(started_at) > Duration::from_secs(10) {
break; break;
} }
} }
sleep(Duration::from_secs(15));
} }
async fn rpc_getslot_source(rpc_url: Url, slot_source: SlotSource, mpsc_downstream: tokio::sync::mpsc::Sender<SlotDatapoint>) { async fn rpc_getslot_source(rpc_url: Url, slot_source: SlotSource, mpsc_downstream: tokio::sync::mpsc::Sender<SlotDatapoint>) {
@ -201,3 +206,49 @@ pub fn slots() -> SubscribeRequest {
ping: None, ping: None,
} }
} }
async fn visualize_slots(latest_slot_per_source: &HashMap<SlotSource, Slot>) {
// println!("Slots: {:?}", latest_slot_per_source);
let map_source_by_name: HashMap<String, SlotSource> = enum_iterator::all::<SlotSource>()
.map(|check| (format!("{:?}", check), check))
.collect();
let sorted_by_time: Vec<(&SlotSource, &Slot)> = latest_slot_per_source.iter().sorted_by_key(|(_, slot)| *slot).collect_vec();
let deltas = sorted_by_time.windows(2).map(|window| {
let (_source1, slot1) = window[0];
let (_source2, slot2) = window[1];
let diff = slot2 - slot1;
diff
}).collect_vec();
for i in 0..(sorted_by_time.len() + deltas.len()) {
if i % 2 == 0 {
let (source, slot) = sorted_by_time.get(i / 2).unwrap();
print!("{}({:?})", slot, source);
} else {
let edge = deltas.get(i / 2).unwrap().clone();
if edge > 0 {
print!(" {} ", ".".repeat(edge as usize));
} else {
print!(" = ");
}
}
}
let all_sources: HashSet<SlotSource> = map_source_by_name.values().cloned().collect();
let sources_with_data: HashSet<SlotSource> = latest_slot_per_source.keys().cloned().collect();
let no_data_sources = all_sources.difference(&sources_with_data).collect_vec();
if no_data_sources.is_empty() {
print!(" // all sources have data");
} else {
print!(" // no data from {:?}", no_data_sources);
}
println!();
}