Async connection creation in connection cache (#33302)

If there is a connection in the cache available, use it and create the additional connection asynchronously.
This commit is contained in:
Lijun Wang 2023-09-25 18:17:47 -07:00 committed by GitHub
parent d25d53e979
commit 344e466e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 236 additions and 71 deletions

1
Cargo.lock generated
View File

@ -5700,6 +5700,7 @@ version = "1.17.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bincode", "bincode",
"crossbeam-channel",
"futures-util", "futures-util",
"indexmap 2.0.0", "indexmap 2.0.0",
"indicatif", "indicatif",

View File

@ -1,7 +1,9 @@
use { use {
crate::bench_tps_client::{BenchTpsClient, BenchTpsError, Result}, crate::bench_tps_client::{BenchTpsClient, BenchTpsError, Result},
solana_client::tpu_client::TpuClient, solana_client::tpu_client::TpuClient,
solana_connection_cache::connection_cache::{ConnectionManager, ConnectionPool}, solana_connection_cache::connection_cache::{
ConnectionManager, ConnectionPool, NewConnectionConfig,
},
solana_sdk::{ solana_sdk::{
account::Account, commitment_config::CommitmentConfig, epoch_info::EpochInfo, hash::Hash, account::Account, commitment_config::CommitmentConfig, epoch_info::EpochInfo, hash::Hash,
message::Message, pubkey::Pubkey, signature::Signature, transaction::Transaction, message::Message, pubkey::Pubkey, signature::Signature, transaction::Transaction,
@ -12,6 +14,7 @@ impl<P, M, C> BenchTpsClient for TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
fn send_transaction(&self, transaction: Transaction) -> Result<Signature> { fn send_transaction(&self, transaction: Transaction) -> Result<Signature> {
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];

View File

@ -5,6 +5,7 @@ use {
client_connection::ClientConnection, client_connection::ClientConnection,
connection_cache::{ connection_cache::{
BaseClientConnection, ConnectionCache as BackendConnectionCache, ConnectionPool, BaseClientConnection, ConnectionCache as BackendConnectionCache, ConnectionPool,
NewConnectionConfig,
}, },
}, },
solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool}, solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool},

View File

@ -3,6 +3,7 @@ use {
crate::{connection_cache::ConnectionCache, tpu_client::TpuClientConfig}, crate::{connection_cache::ConnectionCache, tpu_client::TpuClientConfig},
solana_connection_cache::connection_cache::{ solana_connection_cache::connection_cache::{
ConnectionCache as BackendConnectionCache, ConnectionManager, ConnectionPool, ConnectionCache as BackendConnectionCache, ConnectionManager, ConnectionPool,
NewConnectionConfig,
}, },
solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool}, solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool},
solana_rpc_client::nonblocking::rpc_client::RpcClient, solana_rpc_client::nonblocking::rpc_client::RpcClient,
@ -30,6 +31,7 @@ impl<P, M, C> TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Serialize and send transaction to the current and upcoming leader TPUs according to fanout /// Serialize and send transaction to the current and upcoming leader TPUs according to fanout
/// size /// size
@ -99,6 +101,7 @@ impl<P, M, C> TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Create a new client that disconnects when dropped /// Create a new client that disconnects when dropped
pub async fn new_with_connection_cache( pub async fn new_with_connection_cache(

View File

@ -2,6 +2,7 @@ use {
crate::connection_cache::ConnectionCache, crate::connection_cache::ConnectionCache,
solana_connection_cache::connection_cache::{ solana_connection_cache::connection_cache::{
ConnectionCache as BackendConnectionCache, ConnectionManager, ConnectionPool, ConnectionCache as BackendConnectionCache, ConnectionManager, ConnectionPool,
NewConnectionConfig,
}, },
solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool}, solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool},
solana_rpc_client::rpc_client::RpcClient, solana_rpc_client::rpc_client::RpcClient,
@ -34,6 +35,7 @@ impl<P, M, C> TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Serialize and send transaction to the current and upcoming leader TPUs according to fanout /// Serialize and send transaction to the current and upcoming leader TPUs according to fanout
/// size /// size
@ -90,6 +92,7 @@ impl<P, M, C> TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Create a new client that disconnects when dropped /// Create a new client that disconnects when dropped
pub fn new_with_connection_cache( pub fn new_with_connection_cache(

View File

@ -12,6 +12,7 @@ edition = { workspace = true }
[dependencies] [dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
bincode = { workspace = true } bincode = { workspace = true }
crossbeam-channel = { workspace = true }
futures-util = { workspace = true } futures-util = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
indicatif = { workspace = true, optional = true } indicatif = { workspace = true, optional = true }

View File

@ -4,13 +4,16 @@ use {
connection_cache_stats::{ConnectionCacheStats, CONNECTION_STAT_SUBMISSION_INTERVAL}, connection_cache_stats::{ConnectionCacheStats, CONNECTION_STAT_SUBMISSION_INTERVAL},
nonblocking::client_connection::ClientConnection as NonblockingClientConnection, nonblocking::client_connection::ClientConnection as NonblockingClientConnection,
}, },
crossbeam_channel::{Receiver, RecvError, Sender},
indexmap::map::IndexMap, indexmap::map::IndexMap,
log::*,
rand::{thread_rng, Rng}, rand::{thread_rng, Rng},
solana_measure::measure::Measure, solana_measure::measure::Measure,
solana_sdk::timing::AtomicInterval, solana_sdk::timing::AtomicInterval,
std::{ std::{
net::SocketAddr, net::SocketAddr,
sync::{atomic::Ordering, Arc, RwLock}, sync::{atomic::Ordering, Arc, RwLock},
thread::{Builder, JoinHandle},
}, },
thiserror::Error, thiserror::Error,
}; };
@ -27,9 +30,9 @@ pub enum Protocol {
QUIC, QUIC,
} }
pub trait ConnectionManager { pub trait ConnectionManager: Send + Sync + 'static {
type ConnectionPool: ConnectionPool; type ConnectionPool: ConnectionPool;
type NewConnectionConfig; type NewConnectionConfig: NewConnectionConfig;
const PROTOCOL: Protocol; const PROTOCOL: Protocol;
@ -43,18 +46,20 @@ pub struct ConnectionCache<
T, // NewConnectionConfig T, // NewConnectionConfig
> { > {
name: &'static str, name: &'static str,
map: RwLock<IndexMap<SocketAddr, /*ConnectionPool:*/ R>>, map: Arc<RwLock<IndexMap<SocketAddr, /*ConnectionPool:*/ R>>>,
connection_manager: S, connection_manager: Arc<S>,
stats: Arc<ConnectionCacheStats>, stats: Arc<ConnectionCacheStats>,
last_stats: AtomicInterval, last_stats: AtomicInterval,
connection_pool_size: usize, connection_pool_size: usize,
connection_config: T, connection_config: Arc<T>,
sender: Sender<(usize, SocketAddr)>,
} }
impl<P, M, C> ConnectionCache<P, M, C> impl<P, M, C> ConnectionCache<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
pub fn new( pub fn new(
name: &'static str, name: &'static str,
@ -76,17 +81,61 @@ where
connection_config: C, connection_config: C,
connection_manager: M, connection_manager: M,
) -> Self { ) -> Self {
let (sender, receiver) = crossbeam_channel::unbounded();
let map = Arc::new(RwLock::new(IndexMap::with_capacity(MAX_CONNECTIONS)));
let config = Arc::new(connection_config);
let connection_manager = Arc::new(connection_manager);
let connection_pool_size = 1.max(connection_pool_size); // The minimum pool size is 1.
let stats = Arc::new(ConnectionCacheStats::default());
let _async_connection_thread =
Self::create_connection_async_thread(map.clone(), receiver, stats.clone());
Self { Self {
name, name,
map: RwLock::new(IndexMap::with_capacity(MAX_CONNECTIONS)), map,
stats: Arc::new(ConnectionCacheStats::default()), stats,
connection_manager, connection_manager,
last_stats: AtomicInterval::default(), last_stats: AtomicInterval::default(),
connection_pool_size: 1.max(connection_pool_size), // The minimum pool size is 1. connection_pool_size,
connection_config, connection_config: config,
sender,
} }
} }
/// This actually triggers the connection creation by sending empty data
fn create_connection_async_thread(
map: Arc<RwLock<IndexMap<SocketAddr, /*ConnectionPool:*/ P>>>,
receiver: Receiver<(usize, SocketAddr)>,
stats: Arc<ConnectionCacheStats>,
) -> JoinHandle<()> {
Builder::new()
.name("solQAsynCon".to_string())
.spawn(move || loop {
let recv_result = receiver.recv();
match recv_result {
Err(RecvError) => {
break;
}
Ok((idx, addr)) => {
let map = map.read().unwrap();
let pool = map.get(&addr);
if let Some(pool) = pool {
let conn = pool.get(idx);
if let Ok(conn) = conn {
drop(map);
let conn = conn.new_blocking_connection(addr, stats.clone());
let result = conn.send_data(&[]);
debug!("Create async connection result {result:?} for {addr}");
}
}
}
}
})
.unwrap()
}
/// Create a lazy connection object under the exclusive lock of the cache map if there is not /// Create a lazy connection object under the exclusive lock of the cache map if there is not
/// enough used connections in the connection pool for the specified address. /// enough used connections in the connection pool for the specified address.
/// Returns CreateConnectionResult. /// Returns CreateConnectionResult.
@ -102,47 +151,37 @@ where
// Read again, as it is possible that between read lock dropped and the write lock acquired // Read again, as it is possible that between read lock dropped and the write lock acquired
// another thread could have setup the connection. // another thread could have setup the connection.
let should_create_connection = map let pool_status = map
.get(addr) .get(addr)
.map(|pool| pool.need_new_connection(self.connection_pool_size)) .map(|pool| pool.check_pool_status(self.connection_pool_size))
.unwrap_or(true); .unwrap_or(PoolStatus::Empty);
let (cache_hit, num_evictions, eviction_timing_ms) = if should_create_connection { let (cache_hit, num_evictions, eviction_timing_ms) =
// evict a connection if the cache is reaching upper bounds if matches!(pool_status, PoolStatus::Empty) {
let mut num_evictions = 0; Self::create_connection_internal(
let mut get_connection_cache_eviction_measure = &self.connection_config,
Measure::start("get_connection_cache_eviction_measure"); &self.connection_manager,
let existing_index = map.get_index_of(addr); &mut map,
while map.len() >= MAX_CONNECTIONS { addr,
let mut rng = thread_rng(); self.connection_pool_size,
let n = rng.gen_range(0..MAX_CONNECTIONS); None,
if let Some(index) = existing_index { )
if n == index { } else {
continue; (true, 0, 0)
} };
}
map.swap_remove_index(n);
num_evictions += 1;
}
get_connection_cache_eviction_measure.stop();
map.entry(*addr) if matches!(pool_status, PoolStatus::PartiallyFull) {
.and_modify(|pool| { // trigger an async connection create
pool.add_connection(&self.connection_config, addr); debug!("Triggering async connection for {addr:?}");
}) Self::create_connection_internal(
.or_insert_with(|| { &self.connection_config,
let mut pool = self.connection_manager.new_connection_pool(); &self.connection_manager,
pool.add_connection(&self.connection_config, addr); &mut map,
pool addr,
}); self.connection_pool_size,
( Some(&self.sender),
false, );
num_evictions, }
get_connection_cache_eviction_measure.as_ms(),
)
} else {
(true, 0, 0)
};
let pool = map.get(addr).unwrap(); let pool = map.get(addr).unwrap();
let connection = pool.borrow_connection(); let connection = pool.borrow_connection();
@ -156,6 +195,63 @@ where
} }
} }
fn create_connection_internal(
config: &Arc<C>,
connection_manager: &Arc<M>,
map: &mut std::sync::RwLockWriteGuard<'_, IndexMap<SocketAddr, P>>,
addr: &SocketAddr,
connection_pool_size: usize,
async_connection_sender: Option<&Sender<(usize, SocketAddr)>>,
) -> (bool, u64, u64) {
// 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");
let existing_index = map.get_index_of(addr);
while map.len() >= MAX_CONNECTIONS {
let mut rng = thread_rng();
let n = rng.gen_range(0..MAX_CONNECTIONS);
if let Some(index) = existing_index {
if n == index {
continue;
}
}
map.swap_remove_index(n);
num_evictions += 1;
}
get_connection_cache_eviction_measure.stop();
let mut hit_cache = false;
map.entry(*addr)
.and_modify(|pool| {
if matches!(
pool.check_pool_status(connection_pool_size),
PoolStatus::PartiallyFull
) {
let idx = pool.add_connection(config, addr);
if let Some(sender) = async_connection_sender {
debug!(
"Sending async connection creation {} for {addr}",
pool.num_connections() - 1
);
sender.send((idx, *addr)).unwrap();
};
} else {
hit_cache = true;
}
})
.or_insert_with(|| {
let mut pool = connection_manager.new_connection_pool();
pool.add_connection(config, addr);
pool
});
(
hit_cache,
num_evictions,
get_connection_cache_eviction_measure.as_ms(),
)
}
fn get_or_add_connection( fn get_or_add_connection(
&self, &self,
addr: &SocketAddr, addr: &SocketAddr,
@ -179,12 +275,26 @@ where
eviction_timing_ms, eviction_timing_ms,
} = match map.get(addr) { } = match map.get(addr) {
Some(pool) => { Some(pool) => {
if pool.need_new_connection(self.connection_pool_size) { let pool_status = pool.check_pool_status(self.connection_pool_size);
if matches!(pool_status, PoolStatus::Empty) {
// create more connection and put it in the pool // create more connection and put it in the pool
drop(map); drop(map);
self.create_connection(&mut lock_timing_ms, addr) self.create_connection(&mut lock_timing_ms, addr)
} else { } else {
let connection = pool.borrow_connection(); let connection = pool.borrow_connection();
if matches!(pool_status, PoolStatus::PartiallyFull) {
debug!("Creating connection async for {addr}");
drop(map);
let mut map = self.map.write().unwrap();
Self::create_connection_internal(
&self.connection_config,
&self.connection_manager,
&mut map,
addr,
self.connection_pool_size,
Some(&self.sender),
);
}
CreateConnectionResult { CreateConnectionResult {
connection, connection,
cache_hit: true, cache_hit: true,
@ -299,12 +409,22 @@ pub enum ClientError {
IoError(#[from] std::io::Error), IoError(#[from] std::io::Error),
} }
pub trait ConnectionPool { pub trait NewConnectionConfig: Sized + Send + Sync + 'static {
type NewConnectionConfig; fn new() -> Result<Self, ClientError>;
}
pub enum PoolStatus {
Empty,
PartiallyFull,
Full,
}
pub trait ConnectionPool: Send + Sync + 'static {
type NewConnectionConfig: NewConnectionConfig;
type BaseClientConnection: BaseClientConnection; type BaseClientConnection: BaseClientConnection;
/// Add a connection to the pool /// Add a connection to the pool and return its index
fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr); fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) -> usize;
/// Get the number of current connections in the pool /// Get the number of current connections in the pool
fn num_connections(&self) -> usize; fn num_connections(&self) -> usize;
@ -319,10 +439,17 @@ pub trait ConnectionPool {
let n = rng.gen_range(0..self.num_connections()); let n = rng.gen_range(0..self.num_connections());
self.get(n).expect("index is within num_connections") self.get(n).expect("index is within num_connections")
} }
/// Check if we need to create a new connection. If the count of the connections /// Check if we need to create a new connection. If the count of the connections
/// is smaller than the pool size. /// is smaller than the pool size and if there is no connection at all.
fn need_new_connection(&self, required_pool_size: usize) -> bool { fn check_pool_status(&self, required_pool_size: usize) -> PoolStatus {
self.num_connections() < required_pool_size if self.num_connections() == 0 {
PoolStatus::Empty
} else if self.num_connections() < required_pool_size {
PoolStatus::PartiallyFull
} else {
PoolStatus::Full
}
} }
fn create_pool_entry( fn create_pool_entry(
@ -393,9 +520,16 @@ mod tests {
type NewConnectionConfig = MockUdpConfig; type NewConnectionConfig = MockUdpConfig;
type BaseClientConnection = MockUdp; type BaseClientConnection = MockUdp;
fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) { /// Add a connection into the pool and return its index in the pool.
fn add_connection(
&mut self,
config: &Self::NewConnectionConfig,
addr: &SocketAddr,
) -> usize {
let connection = self.create_pool_entry(config, addr); let connection = self.create_pool_entry(config, addr);
let idx = self.connections.len();
self.connections.push(connection); self.connections.push(connection);
idx
} }
fn num_connections(&self) -> usize { fn num_connections(&self) -> usize {
@ -436,7 +570,7 @@ mod tests {
} }
} }
impl MockUdpConfig { impl NewConnectionConfig for MockUdpConfig {
fn new() -> Result<Self, ClientError> { fn new() -> Result<Self, ClientError> {
Ok(Self { Ok(Self {
udp_socket: Arc::new( udp_socket: Arc::new(

View File

@ -4765,6 +4765,7 @@ version = "1.17.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bincode", "bincode",
"crossbeam-channel",
"futures-util", "futures-util",
"indexmap 2.0.0", "indexmap 2.0.0",
"log", "log",

View File

@ -19,7 +19,7 @@ use {
solana_connection_cache::{ solana_connection_cache::{
connection_cache::{ connection_cache::{
BaseClientConnection, ClientError, ConnectionCache, ConnectionManager, ConnectionPool, BaseClientConnection, ClientError, ConnectionCache, ConnectionManager, ConnectionPool,
ConnectionPoolError, Protocol, ConnectionPoolError, NewConnectionConfig, Protocol,
}, },
connection_cache_stats::ConnectionCacheStats, connection_cache_stats::ConnectionCacheStats,
}, },
@ -53,9 +53,11 @@ impl ConnectionPool for QuicPool {
type BaseClientConnection = Quic; type BaseClientConnection = Quic;
type NewConnectionConfig = QuicConfig; type NewConnectionConfig = QuicConfig;
fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) { fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) -> usize {
let connection = self.create_pool_entry(config, addr); let connection = self.create_pool_entry(config, addr);
let idx = self.connections.len();
self.connections.push(connection); self.connections.push(connection);
idx
} }
fn num_connections(&self) -> usize { fn num_connections(&self) -> usize {
@ -93,8 +95,8 @@ pub struct QuicConfig {
client_endpoint: Option<Endpoint>, client_endpoint: Option<Endpoint>,
} }
impl QuicConfig { impl NewConnectionConfig for QuicConfig {
pub fn new() -> Result<Self, ClientError> { fn new() -> Result<Self, ClientError> {
let (cert, priv_key) = let (cert, priv_key) =
new_self_signed_tls_certificate(&Keypair::new(), IpAddr::V4(Ipv4Addr::UNSPECIFIED))?; new_self_signed_tls_certificate(&Keypair::new(), IpAddr::V4(Ipv4Addr::UNSPECIFIED))?;
Ok(Self { Ok(Self {

View File

@ -340,16 +340,18 @@ impl QuicClient {
Ok(conn) => { Ok(conn) => {
*conn_guard = Some(conn.clone()); *conn_guard = Some(conn.clone());
info!( info!(
"Made connection to {} id {} try_count {}", "Made connection to {} id {} try_count {}, from connection cache warming?: {}",
self.addr, self.addr,
conn.connection.stable_id(), conn.connection.stable_id(),
connection_try_count connection_try_count,
data.is_empty(),
); );
connection_try_count += 1; connection_try_count += 1;
conn.connection.clone() conn.connection.clone()
} }
Err(err) => { Err(err) => {
info!("Cannot make connection to {}, error {:}", self.addr, err); info!("Cannot make connection to {}, error {:}, from connection cache warming?: {}",
self.addr, err, data.is_empty());
return Err(err); return Err(err);
} }
} }

View File

@ -8,7 +8,9 @@ use {
rayon::iter::{IntoParallelIterator, ParallelIterator}, rayon::iter::{IntoParallelIterator, ParallelIterator},
solana_connection_cache::{ solana_connection_cache::{
client_connection::ClientConnection, client_connection::ClientConnection,
connection_cache::{ConnectionCache, ConnectionManager, ConnectionPool}, connection_cache::{
ConnectionCache, ConnectionManager, ConnectionPool, NewConnectionConfig,
},
}, },
solana_rpc_client::rpc_client::RpcClient, solana_rpc_client::rpc_client::RpcClient,
solana_rpc_client_api::{config::RpcProgramAccountsConfig, response::Response}, solana_rpc_client_api::{config::RpcProgramAccountsConfig, response::Response},
@ -124,6 +126,7 @@ impl<P, M, C> ThinClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Create a new ThinClient that will interface with the Rpc at `rpc_addr` using TCP /// Create a new ThinClient that will interface with the Rpc at `rpc_addr` using TCP
/// and the Tpu at `tpu_addr` over `transactions_socket` using Quic or UDP /// and the Tpu at `tpu_addr` over `transactions_socket` using Quic or UDP
@ -324,6 +327,7 @@ impl<P, M, C> Client for ThinClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
fn tpu_addr(&self) -> String { fn tpu_addr(&self) -> String {
self.tpu_addr().to_string() self.tpu_addr().to_string()
@ -334,6 +338,7 @@ impl<P, M, C> SyncClient for ThinClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
fn send_and_confirm_message<T: Signers + ?Sized>( fn send_and_confirm_message<T: Signers + ?Sized>(
&self, &self,
@ -618,6 +623,7 @@ impl<P, M, C> AsyncClient for ThinClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
fn async_send_versioned_transaction( fn async_send_versioned_transaction(
&self, &self,

View File

@ -9,7 +9,7 @@ use {
log::*, log::*,
solana_connection_cache::{ solana_connection_cache::{
connection_cache::{ connection_cache::{
ConnectionCache, ConnectionManager, ConnectionPool, Protocol, ConnectionCache, ConnectionManager, ConnectionPool, NewConnectionConfig, Protocol,
DEFAULT_CONNECTION_POOL_SIZE, DEFAULT_CONNECTION_POOL_SIZE,
}, },
nonblocking::client_connection::ClientConnection, nonblocking::client_connection::ClientConnection,
@ -268,6 +268,7 @@ fn send_wire_transaction_futures<'a, P, M, C>(
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
const SEND_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5); const SEND_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5);
let sleep_duration = SEND_TRANSACTION_INTERVAL.saturating_mul(index as u32); let sleep_duration = SEND_TRANSACTION_INTERVAL.saturating_mul(index as u32);
@ -339,6 +340,7 @@ async fn sleep_and_send_wire_transaction_to_addr<P, M, C>(
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
sleep(sleep_duration).await; sleep(sleep_duration).await;
send_wire_transaction_to_addr(connection_cache, &addr, wire_transaction).await send_wire_transaction_to_addr(connection_cache, &addr, wire_transaction).await
@ -352,6 +354,7 @@ async fn send_wire_transaction_to_addr<P, M, C>(
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
let conn = connection_cache.get_nonblocking_connection(addr); let conn = connection_cache.get_nonblocking_connection(addr);
conn.send_data(&wire_transaction).await conn.send_data(&wire_transaction).await
@ -365,6 +368,7 @@ async fn send_wire_transaction_batch_to_addr<P, M, C>(
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
let conn = connection_cache.get_nonblocking_connection(addr); let conn = connection_cache.get_nonblocking_connection(addr);
conn.send_data_batch(wire_transactions).await conn.send_data_batch(wire_transactions).await
@ -374,6 +378,7 @@ impl<P, M, C> TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Serialize and send transaction to the current and upcoming leader TPUs according to fanout /// Serialize and send transaction to the current and upcoming leader TPUs according to fanout
/// size /// size

View File

@ -3,7 +3,7 @@ use {
crate::nonblocking::tpu_client::TpuClient as NonblockingTpuClient, crate::nonblocking::tpu_client::TpuClient as NonblockingTpuClient,
rayon::iter::{IntoParallelIterator, ParallelIterator}, rayon::iter::{IntoParallelIterator, ParallelIterator},
solana_connection_cache::connection_cache::{ solana_connection_cache::connection_cache::{
ConnectionCache, ConnectionManager, ConnectionPool, ConnectionCache, ConnectionManager, ConnectionPool, NewConnectionConfig,
}, },
solana_rpc_client::rpc_client::RpcClient, solana_rpc_client::rpc_client::RpcClient,
solana_sdk::{clock::Slot, transaction::Transaction, transport::Result as TransportResult}, solana_sdk::{clock::Slot, transaction::Transaction, transport::Result as TransportResult},
@ -71,6 +71,7 @@ impl<P, M, C> TpuClient<P, M, C>
where where
P: ConnectionPool<NewConnectionConfig = C>, P: ConnectionPool<NewConnectionConfig = C>,
M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>, M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
C: NewConnectionConfig,
{ {
/// Serialize and send transaction to the current and upcoming leader TPUs according to fanout /// Serialize and send transaction to the current and upcoming leader TPUs according to fanout
/// size /// size

View File

@ -11,7 +11,7 @@ use {
solana_connection_cache::{ solana_connection_cache::{
connection_cache::{ connection_cache::{
BaseClientConnection, ClientError, ConnectionManager, ConnectionPool, BaseClientConnection, ClientError, ConnectionManager, ConnectionPool,
ConnectionPoolError, Protocol, ConnectionPoolError, NewConnectionConfig, Protocol,
}, },
connection_cache_stats::ConnectionCacheStats, connection_cache_stats::ConnectionCacheStats,
}, },
@ -28,9 +28,11 @@ impl ConnectionPool for UdpPool {
type BaseClientConnection = Udp; type BaseClientConnection = Udp;
type NewConnectionConfig = UdpConfig; type NewConnectionConfig = UdpConfig;
fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) { fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) -> usize {
let connection = self.create_pool_entry(config, addr); let connection = self.create_pool_entry(config, addr);
let idx = self.connections.len();
self.connections.push(connection); self.connections.push(connection);
idx
} }
fn num_connections(&self) -> usize { fn num_connections(&self) -> usize {
@ -57,7 +59,7 @@ pub struct UdpConfig {
udp_socket: Arc<UdpSocket>, udp_socket: Arc<UdpSocket>,
} }
impl UdpConfig { impl NewConnectionConfig for UdpConfig {
fn new() -> Result<Self, ClientError> { fn new() -> Result<Self, ClientError> {
let socket = solana_net_utils::bind_with_any_port(IpAddr::V4(Ipv4Addr::UNSPECIFIED)) let socket = solana_net_utils::bind_with_any_port(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
.map_err(Into::<ClientError>::into)?; .map_err(Into::<ClientError>::into)?;