2022-03-15 15:16:35 -07:00
|
|
|
use {
|
2022-03-21 09:31:37 -07:00
|
|
|
crate::{
|
2022-04-12 20:04:40 -07:00
|
|
|
quic_client::QuicTpuConnection,
|
|
|
|
tpu_connection::{ClientStats, TpuConnection},
|
|
|
|
udp_client::UdpTpuConnection,
|
2022-03-21 09:31:37 -07:00
|
|
|
},
|
2022-05-04 12:38:03 -07:00
|
|
|
indexmap::map::IndexMap,
|
2022-03-15 15:16:35 -07:00
|
|
|
lazy_static::lazy_static,
|
2022-05-02 13:02:49 -07:00
|
|
|
rand::{thread_rng, Rng},
|
2022-04-29 09:58:01 -07:00
|
|
|
solana_measure::measure::Measure,
|
2022-04-12 20:04:40 -07:00
|
|
|
solana_sdk::{
|
|
|
|
timing::AtomicInterval, transaction::VersionedTransaction, transport::TransportError,
|
|
|
|
},
|
2022-03-15 15:16:35 -07:00
|
|
|
std::{
|
2022-05-16 11:13:18 -07:00
|
|
|
net::SocketAddr,
|
2022-04-12 20:04:40 -07:00
|
|
|
sync::{
|
|
|
|
atomic::{AtomicU64, Ordering},
|
2022-05-02 13:02:49 -07:00
|
|
|
Arc, RwLock,
|
2022-04-12 20:04:40 -07:00
|
|
|
},
|
2022-03-15 15:16:35 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// Should be non-zero
|
2022-04-15 12:09:24 -07:00
|
|
|
static MAX_CONNECTIONS: usize = 1024;
|
2022-03-15 15:16:35 -07:00
|
|
|
|
2022-03-24 08:40:26 -07:00
|
|
|
#[derive(Clone)]
|
2022-04-15 12:09:24 -07:00
|
|
|
pub enum Connection {
|
2022-03-24 08:40:26 -07:00
|
|
|
Udp(Arc<UdpTpuConnection>),
|
|
|
|
Quic(Arc<QuicTpuConnection>),
|
|
|
|
}
|
|
|
|
|
2022-04-12 20:04:40 -07:00
|
|
|
#[derive(Default)]
|
|
|
|
struct ConnectionCacheStats {
|
|
|
|
cache_hits: AtomicU64,
|
|
|
|
cache_misses: AtomicU64,
|
2022-05-02 19:27:40 -07:00
|
|
|
cache_evictions: AtomicU64,
|
|
|
|
eviction_time_ms: AtomicU64,
|
2022-04-12 20:04:40 -07:00
|
|
|
sent_packets: AtomicU64,
|
|
|
|
total_batches: AtomicU64,
|
|
|
|
batch_success: AtomicU64,
|
|
|
|
batch_failure: AtomicU64,
|
2022-04-29 09:58:01 -07:00
|
|
|
get_connection_ms: AtomicU64,
|
|
|
|
get_connection_lock_ms: AtomicU64,
|
|
|
|
get_connection_hit_ms: AtomicU64,
|
|
|
|
get_connection_miss_ms: AtomicU64,
|
2022-04-12 20:04:40 -07:00
|
|
|
|
|
|
|
// Need to track these separately per-connection
|
|
|
|
// because we need to track the base stat value from quinn
|
|
|
|
total_client_stats: ClientStats,
|
|
|
|
}
|
|
|
|
|
|
|
|
const CONNECTION_STAT_SUBMISSION_INTERVAL: u64 = 2000;
|
|
|
|
|
|
|
|
impl ConnectionCacheStats {
|
|
|
|
fn add_client_stats(&self, client_stats: &ClientStats, num_packets: usize, is_success: bool) {
|
|
|
|
self.total_client_stats.total_connections.fetch_add(
|
|
|
|
client_stats.total_connections.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed,
|
|
|
|
);
|
|
|
|
self.total_client_stats.connection_reuse.fetch_add(
|
|
|
|
client_stats.connection_reuse.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed,
|
|
|
|
);
|
2022-05-10 09:44:07 -07:00
|
|
|
self.total_client_stats.connection_errors.fetch_add(
|
|
|
|
client_stats.connection_errors.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed,
|
|
|
|
);
|
|
|
|
self.total_client_stats.zero_rtt_accepts.fetch_add(
|
|
|
|
client_stats.zero_rtt_accepts.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed,
|
|
|
|
);
|
|
|
|
self.total_client_stats.zero_rtt_rejects.fetch_add(
|
|
|
|
client_stats.zero_rtt_rejects.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed,
|
|
|
|
);
|
2022-04-12 20:04:40 -07:00
|
|
|
self.sent_packets
|
|
|
|
.fetch_add(num_packets as u64, Ordering::Relaxed);
|
|
|
|
self.total_batches.fetch_add(1, Ordering::Relaxed);
|
|
|
|
if is_success {
|
|
|
|
self.batch_success.fetch_add(1, Ordering::Relaxed);
|
|
|
|
} else {
|
|
|
|
self.batch_failure.fetch_add(1, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn report(&self) {
|
|
|
|
datapoint_info!(
|
|
|
|
"quic-client-connection-stats",
|
|
|
|
(
|
|
|
|
"cache_hits",
|
|
|
|
self.cache_hits.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"cache_misses",
|
|
|
|
self.cache_misses.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
2022-05-02 19:27:40 -07:00
|
|
|
(
|
|
|
|
"cache_evictions",
|
|
|
|
self.cache_evictions.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"eviction_time_ms",
|
|
|
|
self.eviction_time_ms.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
2022-04-29 09:58:01 -07:00
|
|
|
(
|
|
|
|
"get_connection_ms",
|
|
|
|
self.get_connection_ms.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"get_connection_lock_ms",
|
|
|
|
self.get_connection_lock_ms.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"get_connection_hit_ms",
|
|
|
|
self.get_connection_hit_ms.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"get_connection_miss_ms",
|
|
|
|
self.get_connection_miss_ms.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
2022-04-12 20:04:40 -07:00
|
|
|
(
|
|
|
|
"total_connections",
|
|
|
|
self.total_client_stats
|
|
|
|
.total_connections
|
|
|
|
.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"connection_reuse",
|
|
|
|
self.total_client_stats
|
|
|
|
.connection_reuse
|
|
|
|
.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
2022-05-10 09:44:07 -07:00
|
|
|
(
|
|
|
|
"connection_errors",
|
|
|
|
self.total_client_stats
|
|
|
|
.connection_errors
|
|
|
|
.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"zero_rtt_accepts",
|
|
|
|
self.total_client_stats
|
|
|
|
.zero_rtt_accepts
|
|
|
|
.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"zero_rtt_rejects",
|
|
|
|
self.total_client_stats
|
|
|
|
.zero_rtt_rejects
|
|
|
|
.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
2022-04-12 20:04:40 -07:00
|
|
|
(
|
|
|
|
"congestion_events",
|
|
|
|
self.total_client_stats.congestion_events.load_and_reset(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"tx_streams_blocked_uni",
|
|
|
|
self.total_client_stats
|
|
|
|
.tx_streams_blocked_uni
|
|
|
|
.load_and_reset(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"tx_data_blocked",
|
|
|
|
self.total_client_stats.tx_data_blocked.load_and_reset(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"tx_acks",
|
|
|
|
self.total_client_stats.tx_acks.load_and_reset(),
|
|
|
|
i64
|
|
|
|
),
|
2022-04-20 04:59:54 -07:00
|
|
|
(
|
|
|
|
"num_packets",
|
|
|
|
self.sent_packets.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"total_batches",
|
|
|
|
self.total_batches.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"batch_failure",
|
|
|
|
self.batch_failure.swap(0, Ordering::Relaxed),
|
|
|
|
i64
|
|
|
|
),
|
2022-04-12 20:04:40 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-15 12:09:24 -07:00
|
|
|
struct ConnectionMap {
|
2022-05-04 12:38:03 -07:00
|
|
|
map: IndexMap<SocketAddr, Connection>,
|
2022-04-12 20:04:40 -07:00
|
|
|
stats: Arc<ConnectionCacheStats>,
|
|
|
|
last_stats: AtomicInterval,
|
2022-03-21 09:31:37 -07:00
|
|
|
use_quic: bool,
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
|
|
|
|
2022-04-15 12:09:24 -07:00
|
|
|
impl ConnectionMap {
|
2022-03-15 15:16:35 -07:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2022-05-04 12:38:03 -07:00
|
|
|
map: IndexMap::with_capacity(MAX_CONNECTIONS),
|
2022-04-12 20:04:40 -07:00
|
|
|
stats: Arc::new(ConnectionCacheStats::default()),
|
|
|
|
last_stats: AtomicInterval::default(),
|
2022-03-21 09:31:37 -07:00
|
|
|
use_quic: false,
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
|
|
|
}
|
2022-03-21 09:31:37 -07:00
|
|
|
|
|
|
|
pub fn set_use_quic(&mut self, use_quic: bool) {
|
|
|
|
self.use_quic = use_quic;
|
|
|
|
}
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
lazy_static! {
|
2022-05-02 13:02:49 -07:00
|
|
|
static ref CONNECTION_MAP: RwLock<ConnectionMap> = RwLock::new(ConnectionMap::new());
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
|
|
|
|
2022-03-21 09:31:37 -07:00
|
|
|
pub fn set_use_quic(use_quic: bool) {
|
2022-05-02 13:02:49 -07:00
|
|
|
let mut map = (*CONNECTION_MAP).write().unwrap();
|
2022-03-21 09:31:37 -07:00
|
|
|
map.set_use_quic(use_quic);
|
|
|
|
}
|
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
struct GetConnectionResult {
|
|
|
|
connection: Connection,
|
|
|
|
cache_hit: bool,
|
|
|
|
report_stats: bool,
|
2022-05-02 19:27:40 -07:00
|
|
|
map_timing_ms: u64,
|
|
|
|
lock_timing_ms: u64,
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats: Arc<ConnectionCacheStats>,
|
2022-05-02 19:27:40 -07:00
|
|
|
num_evictions: u64,
|
|
|
|
eviction_timing_ms: u64,
|
2022-05-02 13:02:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_or_add_connection(addr: &SocketAddr) -> GetConnectionResult {
|
2022-04-29 09:58:01 -07:00
|
|
|
let mut get_connection_map_lock_measure = Measure::start("get_connection_map_lock_measure");
|
2022-05-02 13:02:49 -07:00
|
|
|
let map = (*CONNECTION_MAP).read().unwrap();
|
2022-04-29 09:58:01 -07:00
|
|
|
get_connection_map_lock_measure.stop();
|
2022-04-06 07:58:32 -07:00
|
|
|
|
2022-05-02 19:27:40 -07:00
|
|
|
let mut lock_timing_ms = get_connection_map_lock_measure.as_ms();
|
2022-05-02 13:02:49 -07:00
|
|
|
|
|
|
|
let report_stats = map
|
2022-04-12 20:04:40 -07:00
|
|
|
.last_stats
|
2022-05-02 13:02:49 -07:00
|
|
|
.should_update(CONNECTION_STAT_SUBMISSION_INTERVAL);
|
2022-04-12 20:04:40 -07:00
|
|
|
|
2022-04-29 09:58:01 -07:00
|
|
|
let mut get_connection_map_measure = Measure::start("get_connection_hit_measure");
|
2022-05-16 11:13:18 -07:00
|
|
|
let (connection, cache_hit, connection_cache_stats, num_evictions, eviction_timing_ms) =
|
|
|
|
match map.map.get(addr) {
|
|
|
|
Some(connection) => (connection.clone(), true, map.stats.clone(), 0, 0),
|
|
|
|
None => {
|
|
|
|
// Upgrade to write access by dropping read lock and acquire write lock
|
|
|
|
drop(map);
|
|
|
|
let mut get_connection_map_lock_measure =
|
|
|
|
Measure::start("get_connection_map_lock_measure");
|
|
|
|
let mut map = (*CONNECTION_MAP).write().unwrap();
|
|
|
|
get_connection_map_lock_measure.stop();
|
2022-05-02 13:02:49 -07:00
|
|
|
|
2022-05-16 11:13:18 -07:00
|
|
|
lock_timing_ms =
|
|
|
|
lock_timing_ms.saturating_add(get_connection_map_lock_measure.as_ms());
|
2022-05-02 13:02:49 -07:00
|
|
|
|
2022-05-16 11:13:18 -07:00
|
|
|
// Read again, as it is possible that between read lock dropped and the write lock acquired
|
|
|
|
// another thread could have setup the connection.
|
|
|
|
match map.map.get(addr) {
|
|
|
|
Some(connection) => (connection.clone(), true, map.stats.clone(), 0, 0),
|
|
|
|
None => {
|
|
|
|
let connection = if map.use_quic {
|
|
|
|
Connection::Quic(Arc::new(QuicTpuConnection::new(*addr)))
|
|
|
|
} else {
|
|
|
|
Connection::Udp(Arc::new(UdpTpuConnection::new(*addr)))
|
|
|
|
};
|
2022-05-02 13:02:49 -07:00
|
|
|
|
2022-05-16 11:13:18 -07:00
|
|
|
// evict a connection if the cache is reaching upper bounds
|
|
|
|
let mut num_evictions = 0;
|
|
|
|
let mut get_connection_cache_eviction_measure =
|
|
|
|
Measure::start("get_connection_cache_eviction_measure");
|
|
|
|
while map.map.len() >= MAX_CONNECTIONS {
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
let n = rng.gen_range(0, MAX_CONNECTIONS);
|
|
|
|
map.map.swap_remove_index(n);
|
|
|
|
num_evictions += 1;
|
|
|
|
}
|
|
|
|
get_connection_cache_eviction_measure.stop();
|
2022-05-14 15:02:13 -07:00
|
|
|
|
2022-05-16 11:13:18 -07:00
|
|
|
map.map.insert(*addr, connection.clone());
|
|
|
|
(
|
|
|
|
connection,
|
|
|
|
false,
|
|
|
|
map.stats.clone(),
|
|
|
|
num_evictions,
|
|
|
|
get_connection_cache_eviction_measure.as_ms(),
|
|
|
|
)
|
2022-05-14 15:02:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-16 11:13:18 -07:00
|
|
|
};
|
2022-04-29 09:58:01 -07:00
|
|
|
get_connection_map_measure.stop();
|
2022-04-12 20:04:40 -07:00
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
GetConnectionResult {
|
|
|
|
connection,
|
|
|
|
cache_hit,
|
|
|
|
report_stats,
|
2022-05-02 19:27:40 -07:00
|
|
|
map_timing_ms: get_connection_map_measure.as_ms(),
|
|
|
|
lock_timing_ms,
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats,
|
2022-05-02 19:27:40 -07:00
|
|
|
num_evictions,
|
|
|
|
eviction_timing_ms,
|
2022-05-02 13:02:49 -07:00
|
|
|
}
|
|
|
|
}
|
2022-04-12 20:04:40 -07:00
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
// TODO: see https://github.com/solana-labs/solana/issues/23661
|
|
|
|
// remove lazy_static and optimize and refactor this
|
|
|
|
fn get_connection(addr: &SocketAddr) -> (Connection, Arc<ConnectionCacheStats>) {
|
|
|
|
let mut get_connection_measure = Measure::start("get_connection_measure");
|
|
|
|
let GetConnectionResult {
|
|
|
|
connection,
|
|
|
|
cache_hit,
|
|
|
|
report_stats,
|
2022-05-02 19:27:40 -07:00
|
|
|
map_timing_ms,
|
|
|
|
lock_timing_ms,
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats,
|
2022-05-02 19:27:40 -07:00
|
|
|
num_evictions,
|
|
|
|
eviction_timing_ms,
|
2022-05-02 13:02:49 -07:00
|
|
|
} = get_or_add_connection(addr);
|
|
|
|
|
2022-05-16 11:13:18 -07:00
|
|
|
let other_stats = if let Connection::Quic(conn) = &connection {
|
|
|
|
conn.stats().map(|s| (conn.base_stats(), s))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
if report_stats {
|
|
|
|
connection_cache_stats.report();
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some((connection_stats, new_stats)) = other_stats {
|
|
|
|
connection_cache_stats
|
|
|
|
.total_client_stats
|
|
|
|
.congestion_events
|
|
|
|
.update_stat(
|
|
|
|
&connection_stats.congestion_events,
|
|
|
|
new_stats.path.congestion_events,
|
|
|
|
);
|
|
|
|
|
|
|
|
connection_cache_stats
|
2022-04-12 20:04:40 -07:00
|
|
|
.total_client_stats
|
|
|
|
.tx_streams_blocked_uni
|
|
|
|
.update_stat(
|
|
|
|
&connection_stats.tx_streams_blocked_uni,
|
|
|
|
new_stats.frame_tx.streams_blocked_uni,
|
|
|
|
);
|
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats
|
|
|
|
.total_client_stats
|
|
|
|
.tx_data_blocked
|
|
|
|
.update_stat(
|
|
|
|
&connection_stats.tx_data_blocked,
|
|
|
|
new_stats.frame_tx.data_blocked,
|
|
|
|
);
|
2022-04-12 20:04:40 -07:00
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats
|
2022-04-12 20:04:40 -07:00
|
|
|
.total_client_stats
|
|
|
|
.tx_acks
|
|
|
|
.update_stat(&connection_stats.tx_acks, new_stats.frame_tx.acks);
|
|
|
|
}
|
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
if cache_hit {
|
|
|
|
connection_cache_stats
|
|
|
|
.cache_hits
|
|
|
|
.fetch_add(1, Ordering::Relaxed);
|
|
|
|
connection_cache_stats
|
2022-04-29 09:58:01 -07:00
|
|
|
.get_connection_hit_ms
|
2022-05-02 19:27:40 -07:00
|
|
|
.fetch_add(map_timing_ms, Ordering::Relaxed);
|
2022-04-12 20:04:40 -07:00
|
|
|
} else {
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats
|
|
|
|
.cache_misses
|
|
|
|
.fetch_add(1, Ordering::Relaxed);
|
|
|
|
connection_cache_stats
|
2022-04-29 09:58:01 -07:00
|
|
|
.get_connection_miss_ms
|
2022-05-02 19:27:40 -07:00
|
|
|
.fetch_add(map_timing_ms, Ordering::Relaxed);
|
|
|
|
connection_cache_stats
|
|
|
|
.cache_evictions
|
|
|
|
.fetch_add(num_evictions, Ordering::Relaxed);
|
|
|
|
connection_cache_stats
|
|
|
|
.eviction_time_ms
|
|
|
|
.fetch_add(eviction_timing_ms, Ordering::Relaxed);
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
2022-05-02 13:02:49 -07:00
|
|
|
|
2022-04-29 09:58:01 -07:00
|
|
|
get_connection_measure.stop();
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats
|
2022-04-29 09:58:01 -07:00
|
|
|
.get_connection_lock_ms
|
2022-05-02 19:27:40 -07:00
|
|
|
.fetch_add(lock_timing_ms, Ordering::Relaxed);
|
2022-05-02 13:02:49 -07:00
|
|
|
connection_cache_stats
|
2022-04-29 09:58:01 -07:00
|
|
|
.get_connection_ms
|
2022-05-02 13:02:49 -07:00
|
|
|
.fetch_add(get_connection_measure.as_ms(), Ordering::Relaxed);
|
|
|
|
(connection, connection_cache_stats)
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
|
|
|
|
2022-03-24 08:40:26 -07:00
|
|
|
// TODO: see https://github.com/solana-labs/solana/issues/23851
|
|
|
|
// use enum_dispatch and get rid of this tedious code.
|
|
|
|
// The main blocker to using enum_dispatch right now is that
|
|
|
|
// the it doesn't work with static methods like TpuConnection::new
|
|
|
|
// which is used by thin_client. This will be eliminated soon
|
|
|
|
// once thin_client is moved to using this connection cache.
|
|
|
|
// Once that is done, we will migrate to using enum_dispatch
|
|
|
|
// This will be done in a followup to
|
|
|
|
// https://github.com/solana-labs/solana/pull/23817
|
|
|
|
pub fn send_wire_transaction_batch(
|
|
|
|
packets: &[&[u8]],
|
|
|
|
addr: &SocketAddr,
|
|
|
|
) -> Result<(), TransportError> {
|
2022-04-12 20:04:40 -07:00
|
|
|
let (conn, stats) = get_connection(addr);
|
|
|
|
let client_stats = ClientStats::default();
|
|
|
|
let r = match conn {
|
|
|
|
Connection::Udp(conn) => conn.send_wire_transaction_batch(packets, &client_stats),
|
|
|
|
Connection::Quic(conn) => conn.send_wire_transaction_batch(packets, &client_stats),
|
|
|
|
};
|
|
|
|
stats.add_client_stats(&client_stats, packets.len(), r.is_ok());
|
|
|
|
r
|
2022-03-24 08:40:26 -07:00
|
|
|
}
|
|
|
|
|
2022-04-12 07:15:59 -07:00
|
|
|
pub fn send_wire_transaction_async(
|
|
|
|
packets: Vec<u8>,
|
|
|
|
addr: &SocketAddr,
|
|
|
|
) -> Result<(), TransportError> {
|
2022-04-12 20:04:40 -07:00
|
|
|
let (conn, stats) = get_connection(addr);
|
|
|
|
let client_stats = Arc::new(ClientStats::default());
|
|
|
|
let r = match conn {
|
|
|
|
Connection::Udp(conn) => conn.send_wire_transaction_async(packets, client_stats.clone()),
|
|
|
|
Connection::Quic(conn) => conn.send_wire_transaction_async(packets, client_stats.clone()),
|
|
|
|
};
|
|
|
|
stats.add_client_stats(&client_stats, 1, r.is_ok());
|
|
|
|
r
|
2022-04-12 07:15:59 -07:00
|
|
|
}
|
|
|
|
|
2022-04-14 19:20:34 -07:00
|
|
|
pub fn send_wire_transaction_batch_async(
|
|
|
|
packets: Vec<Vec<u8>>,
|
|
|
|
addr: &SocketAddr,
|
|
|
|
) -> Result<(), TransportError> {
|
|
|
|
let (conn, stats) = get_connection(addr);
|
|
|
|
let client_stats = Arc::new(ClientStats::default());
|
|
|
|
let len = packets.len();
|
|
|
|
let r = match conn {
|
|
|
|
Connection::Udp(conn) => {
|
|
|
|
conn.send_wire_transaction_batch_async(packets, client_stats.clone())
|
|
|
|
}
|
|
|
|
Connection::Quic(conn) => {
|
|
|
|
conn.send_wire_transaction_batch_async(packets, client_stats.clone())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
stats.add_client_stats(&client_stats, len, r.is_ok());
|
|
|
|
r
|
|
|
|
}
|
|
|
|
|
2022-03-24 08:40:26 -07:00
|
|
|
pub fn send_wire_transaction(
|
|
|
|
wire_transaction: &[u8],
|
|
|
|
addr: &SocketAddr,
|
|
|
|
) -> Result<(), TransportError> {
|
2022-04-12 20:04:40 -07:00
|
|
|
send_wire_transaction_batch(&[wire_transaction], addr)
|
2022-03-24 08:40:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serialize_and_send_transaction(
|
|
|
|
transaction: &VersionedTransaction,
|
|
|
|
addr: &SocketAddr,
|
|
|
|
) -> Result<(), TransportError> {
|
2022-04-12 20:04:40 -07:00
|
|
|
let (conn, stats) = get_connection(addr);
|
|
|
|
let client_stats = ClientStats::default();
|
|
|
|
let r = match conn {
|
|
|
|
Connection::Udp(conn) => conn.serialize_and_send_transaction(transaction, &client_stats),
|
|
|
|
Connection::Quic(conn) => conn.serialize_and_send_transaction(transaction, &client_stats),
|
|
|
|
};
|
|
|
|
stats.add_client_stats(&client_stats, 1, r.is_ok());
|
|
|
|
r
|
2022-03-24 08:40:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn par_serialize_and_send_transaction_batch(
|
|
|
|
transactions: &[VersionedTransaction],
|
|
|
|
addr: &SocketAddr,
|
|
|
|
) -> Result<(), TransportError> {
|
2022-04-12 20:04:40 -07:00
|
|
|
let (conn, stats) = get_connection(addr);
|
|
|
|
let client_stats = ClientStats::default();
|
|
|
|
let r = match conn {
|
|
|
|
Connection::Udp(conn) => {
|
|
|
|
conn.par_serialize_and_send_transaction_batch(transactions, &client_stats)
|
|
|
|
}
|
|
|
|
Connection::Quic(conn) => {
|
|
|
|
conn.par_serialize_and_send_transaction_batch(transactions, &client_stats)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
stats.add_client_stats(&client_stats, transactions.len(), r.is_ok());
|
|
|
|
r
|
2022-03-24 08:40:26 -07:00
|
|
|
}
|
|
|
|
|
2022-03-15 15:16:35 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use {
|
2022-03-24 08:40:26 -07:00
|
|
|
crate::{
|
|
|
|
connection_cache::{get_connection, Connection, CONNECTION_MAP, MAX_CONNECTIONS},
|
|
|
|
tpu_connection::TpuConnection,
|
|
|
|
},
|
2022-03-15 15:16:35 -07:00
|
|
|
rand::{Rng, SeedableRng},
|
|
|
|
rand_chacha::ChaChaRng,
|
2022-03-24 08:40:26 -07:00
|
|
|
std::net::{IpAddr, SocketAddr},
|
2022-03-15 15:16:35 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
fn get_addr(rng: &mut ChaChaRng) -> SocketAddr {
|
|
|
|
let a = rng.gen_range(1, 255);
|
|
|
|
let b = rng.gen_range(1, 255);
|
|
|
|
let c = rng.gen_range(1, 255);
|
|
|
|
let d = rng.gen_range(1, 255);
|
|
|
|
|
|
|
|
let addr_str = format!("{}.{}.{}.{}:80", a, b, c, d);
|
|
|
|
|
|
|
|
addr_str.parse().expect("Invalid address")
|
|
|
|
}
|
|
|
|
|
2022-03-24 08:40:26 -07:00
|
|
|
fn ip(conn: Connection) -> IpAddr {
|
|
|
|
match conn {
|
|
|
|
Connection::Udp(conn) => conn.tpu_addr().ip(),
|
|
|
|
Connection::Quic(conn) => conn.tpu_addr().ip(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-15 15:16:35 -07:00
|
|
|
#[test]
|
|
|
|
fn test_connection_cache() {
|
2022-04-15 12:09:24 -07:00
|
|
|
solana_logger::setup();
|
2022-03-15 15:16:35 -07:00
|
|
|
// Allow the test to run deterministically
|
|
|
|
// with the same pseudorandom sequence between runs
|
|
|
|
// and on different platforms - the cryptographic security
|
|
|
|
// property isn't important here but ChaChaRng provides a way
|
|
|
|
// to get the same pseudorandom sequence on different platforms
|
|
|
|
let mut rng = ChaChaRng::seed_from_u64(42);
|
|
|
|
|
|
|
|
// Generate a bunch of random addresses and create TPUConnections to them
|
|
|
|
// Since TPUConnection::new is infallible, it should't matter whether or not
|
|
|
|
// we can actually connect to those addresses - TPUConnection implementations should either
|
|
|
|
// be lazy and not connect until first use or handle connection errors somehow
|
|
|
|
// (without crashing, as would be required in a real practical validator)
|
|
|
|
let addrs = (0..MAX_CONNECTIONS)
|
|
|
|
.into_iter()
|
|
|
|
.map(|_| {
|
|
|
|
let addr = get_addr(&mut rng);
|
|
|
|
get_connection(&addr);
|
|
|
|
addr
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
{
|
2022-05-02 13:02:49 -07:00
|
|
|
let map = (*CONNECTION_MAP).read().unwrap();
|
|
|
|
assert!(map.map.len() == MAX_CONNECTIONS);
|
2022-03-15 15:16:35 -07:00
|
|
|
addrs.iter().for_each(|a| {
|
2022-05-02 13:02:49 -07:00
|
|
|
let conn = map.map.get(a).expect("Address not found");
|
2022-04-06 07:58:32 -07:00
|
|
|
assert!(a.ip() == ip(conn.clone()));
|
2022-03-15 15:16:35 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-05-02 13:02:49 -07:00
|
|
|
let addr = get_addr(&mut rng);
|
|
|
|
get_connection(&addr);
|
|
|
|
|
|
|
|
let map = (*CONNECTION_MAP).read().unwrap();
|
|
|
|
assert!(map.map.len() == MAX_CONNECTIONS);
|
|
|
|
let _conn = map.map.get(&addr).expect("Address not found");
|
2022-03-15 15:16:35 -07:00
|
|
|
}
|
|
|
|
}
|