Use LRU in connection-cache (#24109)
Switch to using LRU for connection-cache
This commit is contained in:
		
							
								
								
									
										5
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -2384,9 +2384,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "lru"
 | 
					name = "lru"
 | 
				
			||||||
version = "0.7.3"
 | 
					version = "0.7.5"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "fcb87f3080f6d1d69e8c564c0fcfde1d7aa8cc451ce40cae89479111f03bc0eb"
 | 
					checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "hashbrown",
 | 
					 "hashbrown",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -4622,6 +4622,7 @@ dependencies = [
 | 
				
			|||||||
 "jsonrpc-http-server",
 | 
					 "jsonrpc-http-server",
 | 
				
			||||||
 "lazy_static",
 | 
					 "lazy_static",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 | 
					 "lru",
 | 
				
			||||||
 "quinn",
 | 
					 "quinn",
 | 
				
			||||||
 "rand 0.7.3",
 | 
					 "rand 0.7.3",
 | 
				
			||||||
 "rand_chacha 0.2.2",
 | 
					 "rand_chacha 0.2.2",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ itertools = "0.10.2"
 | 
				
			|||||||
jsonrpc-core = "18.0.0"
 | 
					jsonrpc-core = "18.0.0"
 | 
				
			||||||
lazy_static = "1.4.0"
 | 
					lazy_static = "1.4.0"
 | 
				
			||||||
log = "0.4.14"
 | 
					log = "0.4.14"
 | 
				
			||||||
 | 
					lru = "0.7.5"
 | 
				
			||||||
quinn = "0.8.0"
 | 
					quinn = "0.8.0"
 | 
				
			||||||
rand = "0.7.0"
 | 
					rand = "0.7.0"
 | 
				
			||||||
rand_chacha = "0.2.2"
 | 
					rand_chacha = "0.2.2"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,10 @@ use {
 | 
				
			|||||||
        quic_client::QuicTpuConnection, tpu_connection::TpuConnection, udp_client::UdpTpuConnection,
 | 
					        quic_client::QuicTpuConnection, tpu_connection::TpuConnection, udp_client::UdpTpuConnection,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lazy_static::lazy_static,
 | 
					    lazy_static::lazy_static,
 | 
				
			||||||
 | 
					    lru::LruCache,
 | 
				
			||||||
    solana_net_utils::VALIDATOR_PORT_RANGE,
 | 
					    solana_net_utils::VALIDATOR_PORT_RANGE,
 | 
				
			||||||
    solana_sdk::{transaction::VersionedTransaction, transport::TransportError},
 | 
					    solana_sdk::{transaction::VersionedTransaction, transport::TransportError},
 | 
				
			||||||
    std::{
 | 
					    std::{
 | 
				
			||||||
        collections::{hash_map::Entry, BTreeMap, HashMap},
 | 
					 | 
				
			||||||
        net::{IpAddr, Ipv4Addr, SocketAddr},
 | 
					        net::{IpAddr, Ipv4Addr, SocketAddr},
 | 
				
			||||||
        sync::{Arc, Mutex},
 | 
					        sync::{Arc, Mutex},
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -22,26 +22,14 @@ enum Connection {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ConnMap {
 | 
					struct ConnMap {
 | 
				
			||||||
    // Keeps track of the connection associated with an addr and the last time it was used
 | 
					    map: LruCache<SocketAddr, Connection>,
 | 
				
			||||||
    map: HashMap<SocketAddr, (Connection, u64)>,
 | 
					 | 
				
			||||||
    // Helps to find the least recently used connection. The search and inserts are O(log(n))
 | 
					 | 
				
			||||||
    // but since we're bounding the size of the collections, this should be constant
 | 
					 | 
				
			||||||
    // (and hopefully negligible) time. In theory, we can do this in constant time
 | 
					 | 
				
			||||||
    // with a queue implemented as a doubly-linked list (and all the
 | 
					 | 
				
			||||||
    // HashMap entries holding a "pointer" to the corresponding linked-list node),
 | 
					 | 
				
			||||||
    // so we can push, pop and bump a used connection back to the end of the queue in O(1) time, but
 | 
					 | 
				
			||||||
    // that seems non-"Rust-y" and low bang/buck. This is still pretty terrible though...
 | 
					 | 
				
			||||||
    last_used_times: BTreeMap<u64, SocketAddr>,
 | 
					 | 
				
			||||||
    ticks: u64,
 | 
					 | 
				
			||||||
    use_quic: bool,
 | 
					    use_quic: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ConnMap {
 | 
					impl ConnMap {
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            map: HashMap::new(),
 | 
					            map: LruCache::new(MAX_CONNECTIONS),
 | 
				
			||||||
            last_used_times: BTreeMap::new(),
 | 
					 | 
				
			||||||
            ticks: 0,
 | 
					 | 
				
			||||||
            use_quic: false,
 | 
					            use_quic: false,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -60,54 +48,29 @@ pub fn set_use_quic(use_quic: bool) {
 | 
				
			|||||||
    map.set_use_quic(use_quic);
 | 
					    map.set_use_quic(use_quic);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[allow(dead_code)]
 | 
					 | 
				
			||||||
// TODO: see https://github.com/solana-labs/solana/issues/23661
 | 
					// TODO: see https://github.com/solana-labs/solana/issues/23661
 | 
				
			||||||
// remove lazy_static and optimize and refactor this
 | 
					// remove lazy_static and optimize and refactor this
 | 
				
			||||||
fn get_connection(addr: &SocketAddr) -> Connection {
 | 
					fn get_connection(addr: &SocketAddr) -> Connection {
 | 
				
			||||||
    let mut map = (*CONNECTION_MAP).lock().unwrap();
 | 
					    let mut map = (*CONNECTION_MAP).lock().unwrap();
 | 
				
			||||||
    let ticks = map.ticks;
 | 
					
 | 
				
			||||||
    let use_quic = map.use_quic;
 | 
					    match map.map.get(addr) {
 | 
				
			||||||
    let (conn, target_ticks) = match map.map.entry(*addr) {
 | 
					        Some(connection) => connection.clone(),
 | 
				
			||||||
        Entry::Occupied(mut entry) => {
 | 
					        None => {
 | 
				
			||||||
            let mut pair = entry.get_mut();
 | 
					 | 
				
			||||||
            let old_ticks = pair.1;
 | 
					 | 
				
			||||||
            pair.1 = ticks;
 | 
					 | 
				
			||||||
            (pair.0.clone(), old_ticks)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Entry::Vacant(entry) => {
 | 
					 | 
				
			||||||
            let (_, send_socket) = solana_net_utils::bind_in_range(
 | 
					            let (_, send_socket) = solana_net_utils::bind_in_range(
 | 
				
			||||||
                IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
 | 
					                IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
 | 
				
			||||||
                VALIDATOR_PORT_RANGE,
 | 
					                VALIDATOR_PORT_RANGE,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
            let conn = if use_quic {
 | 
					            let connection = if map.use_quic {
 | 
				
			||||||
                Connection::Quic(Arc::new(QuicTpuConnection::new(send_socket, *addr)))
 | 
					                Connection::Quic(Arc::new(QuicTpuConnection::new(send_socket, *addr)))
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                Connection::Udp(Arc::new(UdpTpuConnection::new(send_socket, *addr)))
 | 
					                Connection::Udp(Arc::new(UdpTpuConnection::new(send_socket, *addr)))
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            entry.insert((conn.clone(), ticks));
 | 
					            map.map.put(*addr, connection.clone());
 | 
				
			||||||
            (conn, ticks)
 | 
					            connection
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let num_connections = map.map.len();
 | 
					 | 
				
			||||||
    if num_connections > MAX_CONNECTIONS {
 | 
					 | 
				
			||||||
        let (old_ticks, target_addr) = {
 | 
					 | 
				
			||||||
            let (old_ticks, target_addr) = map.last_used_times.iter().next().unwrap();
 | 
					 | 
				
			||||||
            (*old_ticks, *target_addr)
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        map.map.remove(&target_addr);
 | 
					 | 
				
			||||||
        map.last_used_times.remove(&old_ticks);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if target_ticks != ticks {
 | 
					 | 
				
			||||||
        map.last_used_times.remove(&target_ticks);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    map.last_used_times.insert(ticks, *addr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    map.ticks += 1;
 | 
					 | 
				
			||||||
    conn
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: see https://github.com/solana-labs/solana/issues/23851
 | 
					// TODO: see https://github.com/solana-labs/solana/issues/23851
 | 
				
			||||||
@@ -220,11 +183,11 @@ mod tests {
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            let map = (*CONNECTION_MAP).lock().unwrap();
 | 
					            let map = (*CONNECTION_MAP).lock().unwrap();
 | 
				
			||||||
            addrs.iter().for_each(|a| {
 | 
					            addrs.iter().for_each(|a| {
 | 
				
			||||||
                let conn = map.map.get(a).expect("Address not found");
 | 
					                let conn = map.map.peek(a).expect("Address not found");
 | 
				
			||||||
                assert!(a.ip() == ip(conn.0.clone()));
 | 
					                assert!(a.ip() == ip(conn.clone()));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert!(map.map.get(&first_addr).is_none());
 | 
					            assert!(map.map.peek(&first_addr).is_none());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Test that get_connection updates which connection is next up for eviction
 | 
					        // Test that get_connection updates which connection is next up for eviction
 | 
				
			||||||
@@ -237,7 +200,7 @@ mod tests {
 | 
				
			|||||||
        get_connection(&get_addr(&mut rng));
 | 
					        get_connection(&get_addr(&mut rng));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let map = (*CONNECTION_MAP).lock().unwrap();
 | 
					        let map = (*CONNECTION_MAP).lock().unwrap();
 | 
				
			||||||
        assert!(map.map.get(&addrs[0]).is_some());
 | 
					        assert!(map.map.peek(&addrs[0]).is_some());
 | 
				
			||||||
        assert!(map.map.get(&addrs[1]).is_none());
 | 
					        assert!(map.map.peek(&addrs[1]).is_none());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -571,13 +571,13 @@ pub(crate) fn submit_gossip_stats(
 | 
				
			|||||||
        .pull
 | 
					        .pull
 | 
				
			||||||
        .votes
 | 
					        .votes
 | 
				
			||||||
        .into_iter()
 | 
					        .into_iter()
 | 
				
			||||||
        .map(|(slot, num_votes)| (*slot, *num_votes))
 | 
					        .map(|(slot, num_votes)| (slot, num_votes))
 | 
				
			||||||
        .chain(
 | 
					        .chain(
 | 
				
			||||||
            crds_stats
 | 
					            crds_stats
 | 
				
			||||||
                .push
 | 
					                .push
 | 
				
			||||||
                .votes
 | 
					                .votes
 | 
				
			||||||
                .into_iter()
 | 
					                .into_iter()
 | 
				
			||||||
                .map(|(slot, num_votes)| (*slot, *num_votes)),
 | 
					                .map(|(slot, num_votes)| (slot, num_votes)),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .into_grouping_map()
 | 
					        .into_grouping_map()
 | 
				
			||||||
        .aggregate(|acc, _slot, num_votes| Some(acc.unwrap_or_default() + num_votes));
 | 
					        .aggregate(|acc, _slot, num_votes| Some(acc.unwrap_or_default() + num_votes));
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								programs/bpf/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								programs/bpf/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -1636,6 +1636,15 @@ dependencies = [
 | 
				
			|||||||
 "cfg-if 1.0.0",
 | 
					 "cfg-if 1.0.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "lru"
 | 
				
			||||||
 | 
					version = "0.7.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "hashbrown",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "matches"
 | 
					name = "matches"
 | 
				
			||||||
version = "0.1.9"
 | 
					version = "0.1.9"
 | 
				
			||||||
@@ -3368,6 +3377,7 @@ dependencies = [
 | 
				
			|||||||
 "jsonrpc-core",
 | 
					 "jsonrpc-core",
 | 
				
			||||||
 "lazy_static",
 | 
					 "lazy_static",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 | 
					 "lru",
 | 
				
			||||||
 "quinn",
 | 
					 "quinn",
 | 
				
			||||||
 "rand 0.7.3",
 | 
					 "rand 0.7.3",
 | 
				
			||||||
 "rand_chacha 0.2.2",
 | 
					 "rand_chacha 0.2.2",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user