1use {
5 crate::{
6 packet::{
7 self, PacketBatch, PacketBatchRecycler, PacketRef, PinnedPacketBatch, PACKETS_PER_BATCH,
8 },
9 sendmmsg::{batch_send, SendPktsError},
10 socket::SocketAddrSpace,
11 },
12 crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender, TrySendError},
13 histogram::Histogram,
14 itertools::Itertools,
15 solana_net_utils::multihomed_sockets::{
16 BindIpAddrs, CurrentSocket, FixedSocketProvider, MultihomedSocketProvider, SocketProvider,
17 },
18 solana_packet::Packet,
19 solana_pubkey::Pubkey,
20 solana_time_utils::timestamp,
21 std::{
22 cmp::Reverse,
23 collections::HashMap,
24 net::{IpAddr, UdpSocket},
25 sync::{
26 atomic::{AtomicBool, AtomicUsize, Ordering},
27 Arc,
28 },
29 thread::{sleep, Builder, JoinHandle},
30 time::{Duration, Instant},
31 },
32 thiserror::Error,
33};
34#[cfg(unix)]
35use {
36 nix::poll::{PollFd, PollFlags},
37 std::os::fd::AsFd,
38};
39
40pub trait ChannelSend<T>: Send + 'static {
41 fn send(&self, msg: T) -> std::result::Result<(), SendError<T>>;
42
43 fn try_send(&self, msg: T) -> std::result::Result<(), TrySendError<T>>;
44
45 fn is_empty(&self) -> bool;
46
47 fn len(&self) -> usize;
48}
49
50impl<T> ChannelSend<T> for Sender<T>
51where
52 T: Send + 'static,
53{
54 #[inline]
55 fn send(&self, msg: T) -> std::result::Result<(), SendError<T>> {
56 self.send(msg)
57 }
58
59 #[inline]
60 fn try_send(&self, msg: T) -> std::result::Result<(), TrySendError<T>> {
61 self.try_send(msg)
62 }
63
64 #[inline]
65 fn is_empty(&self) -> bool {
66 self.is_empty()
67 }
68
69 #[inline]
70 fn len(&self) -> usize {
71 self.len()
72 }
73}
74
75pub(crate) const SOCKET_READ_TIMEOUT: Duration = Duration::from_secs(1);
76
77#[derive(Default)]
79pub struct StakedNodes {
80 stakes: Arc<HashMap<Pubkey, u64>>,
81 overrides: HashMap<Pubkey, u64>,
82 total_stake: u64,
83 max_stake: u64,
84 min_stake: u64,
85}
86
87pub type PacketBatchReceiver = Receiver<PacketBatch>;
88pub type PacketBatchSender = Sender<PacketBatch>;
89
90#[derive(Error, Debug)]
91pub enum StreamerError {
92 #[error("I/O error")]
93 Io(#[from] std::io::Error),
94
95 #[error("receive timeout error")]
96 RecvTimeout(#[from] RecvTimeoutError),
97
98 #[error("send packets error")]
99 Send(#[from] SendError<PacketBatch>),
100
101 #[error(transparent)]
102 SendPktsError(#[from] SendPktsError),
103}
104
105pub struct StreamerReceiveStats {
106 pub name: &'static str,
107 pub packets_count: AtomicUsize,
108 pub packet_batches_count: AtomicUsize,
109 pub full_packet_batches_count: AtomicUsize,
110 pub max_channel_len: AtomicUsize,
111 pub num_packets_dropped: AtomicUsize,
112}
113
114impl StreamerReceiveStats {
115 pub fn new(name: &'static str) -> Self {
116 Self {
117 name,
118 packets_count: AtomicUsize::default(),
119 packet_batches_count: AtomicUsize::default(),
120 full_packet_batches_count: AtomicUsize::default(),
121 max_channel_len: AtomicUsize::default(),
122 num_packets_dropped: AtomicUsize::default(),
123 }
124 }
125
126 pub fn report(&self) {
127 datapoint_info!(
128 self.name,
129 (
130 "packets_count",
131 self.packets_count.swap(0, Ordering::Relaxed) as i64,
132 i64
133 ),
134 (
135 "packet_batches_count",
136 self.packet_batches_count.swap(0, Ordering::Relaxed) as i64,
137 i64
138 ),
139 (
140 "full_packet_batches_count",
141 self.full_packet_batches_count.swap(0, Ordering::Relaxed) as i64,
142 i64
143 ),
144 (
145 "channel_len",
146 self.max_channel_len.swap(0, Ordering::Relaxed) as i64,
147 i64
148 ),
149 (
150 "num_packets_dropped",
151 self.num_packets_dropped.swap(0, Ordering::Relaxed) as i64,
152 i64
153 ),
154 );
155 }
156}
157
158pub type Result<T> = std::result::Result<T, StreamerError>;
159
160fn recv_loop<P: SocketProvider>(
161 provider: &mut P,
162 exit: &AtomicBool,
163 packet_batch_sender: &impl ChannelSend<PacketBatch>,
164 recycler: &PacketBatchRecycler,
165 stats: &StreamerReceiveStats,
166 coalesce: Option<Duration>,
167 use_pinned_memory: bool,
168 in_vote_only_mode: Option<Arc<AtomicBool>>,
169 is_staked_service: bool,
170) -> Result<()> {
171 fn setup_socket(socket: &UdpSocket) -> Result<()> {
172 #[cfg(not(unix))]
175 socket.set_read_timeout(Some(SOCKET_READ_TIMEOUT))?;
176
177 #[cfg(unix)]
178 socket.set_nonblocking(true)?;
179
180 Ok(())
181 }
182
183 let mut socket = provider.current_socket_ref();
184 setup_socket(socket)?;
185 #[cfg(unix)]
186 let mut poll_fd = [PollFd::new(socket.as_fd(), PollFlags::POLLIN)];
187
188 loop {
189 let mut packet_batch = if use_pinned_memory {
190 PinnedPacketBatch::new_with_recycler(recycler, PACKETS_PER_BATCH, stats.name)
191 } else {
192 PinnedPacketBatch::with_capacity(PACKETS_PER_BATCH)
193 };
194 packet_batch.resize(PACKETS_PER_BATCH, Packet::default());
195
196 loop {
197 if exit.load(Ordering::Relaxed) {
200 return Ok(());
201 }
202
203 if let Some(ref in_vote_only_mode) = in_vote_only_mode {
204 if in_vote_only_mode.load(Ordering::Relaxed) {
205 sleep(Duration::from_millis(1));
206 continue;
207 }
208 }
209
210 #[cfg(unix)]
211 let result = packet::recv_from(&mut packet_batch, socket, coalesce, &mut poll_fd);
212 #[cfg(not(unix))]
213 let result = packet::recv_from(&mut packet_batch, socket, coalesce);
214
215 if let Ok(len) = result {
216 if len > 0 {
217 let StreamerReceiveStats {
218 packets_count,
219 packet_batches_count,
220 full_packet_batches_count,
221 max_channel_len,
222 ..
223 } = stats;
224
225 packets_count.fetch_add(len, Ordering::Relaxed);
226 packet_batches_count.fetch_add(1, Ordering::Relaxed);
227 max_channel_len.fetch_max(packet_batch_sender.len(), Ordering::Relaxed);
228 if len == PACKETS_PER_BATCH {
229 full_packet_batches_count.fetch_add(1, Ordering::Relaxed);
230 }
231 packet_batch
232 .iter_mut()
233 .for_each(|p| p.meta_mut().set_from_staked_node(is_staked_service));
234 match packet_batch_sender.try_send(packet_batch.into()) {
235 Ok(_) => {}
236 Err(TrySendError::Full(_)) => {
237 stats.num_packets_dropped.fetch_add(len, Ordering::Relaxed);
238 }
239 Err(TrySendError::Disconnected(err)) => {
240 return Err(StreamerError::Send(SendError(err)))
241 }
242 }
243 }
244 break;
245 }
246 }
247
248 if let CurrentSocket::Changed(s) = provider.current_socket() {
249 socket = s;
250 setup_socket(socket)?;
251
252 #[cfg(unix)]
253 {
254 poll_fd = [PollFd::new(socket.as_fd(), PollFlags::POLLIN)];
255 }
256 }
257 }
258}
259
260#[allow(clippy::too_many_arguments)]
261pub fn receiver(
262 thread_name: String,
263 socket: Arc<UdpSocket>,
264 exit: Arc<AtomicBool>,
265 packet_batch_sender: impl ChannelSend<PacketBatch>,
266 recycler: PacketBatchRecycler,
267 stats: Arc<StreamerReceiveStats>,
268 coalesce: Option<Duration>,
269 use_pinned_memory: bool,
270 in_vote_only_mode: Option<Arc<AtomicBool>>,
271 is_staked_service: bool,
272) -> JoinHandle<()> {
273 Builder::new()
274 .name(thread_name)
275 .spawn(move || {
276 let mut provider = FixedSocketProvider::new(socket);
277 let _ = recv_loop(
278 &mut provider,
279 &exit,
280 &packet_batch_sender,
281 &recycler,
282 &stats,
283 coalesce,
284 use_pinned_memory,
285 in_vote_only_mode,
286 is_staked_service,
287 );
288 })
289 .unwrap()
290}
291
292#[allow(clippy::too_many_arguments)]
293pub fn receiver_atomic(
294 thread_name: String,
295 sockets: Arc<[UdpSocket]>,
296 bind_ip_addrs: Arc<BindIpAddrs>,
297 exit: Arc<AtomicBool>,
298 packet_batch_sender: impl ChannelSend<PacketBatch>,
299 recycler: PacketBatchRecycler,
300 stats: Arc<StreamerReceiveStats>,
301 coalesce: Option<Duration>,
302 use_pinned_memory: bool,
303 in_vote_only_mode: Option<Arc<AtomicBool>>,
304 is_staked_service: bool,
305) -> JoinHandle<()> {
306 Builder::new()
307 .name(thread_name)
308 .spawn(move || {
309 let mut provider = MultihomedSocketProvider::new(sockets, bind_ip_addrs);
310 let _ = recv_loop(
311 &mut provider,
312 &exit,
313 &packet_batch_sender,
314 &recycler,
315 &stats,
316 coalesce,
317 use_pinned_memory,
318 in_vote_only_mode,
319 is_staked_service,
320 );
321 })
322 .unwrap()
323}
324
325#[derive(Debug, Default)]
326struct SendStats {
327 bytes: u64,
328 count: u64,
329}
330
331#[derive(Default)]
332struct StreamerSendStats {
333 host_map: HashMap<IpAddr, SendStats>,
334 since: Option<Instant>,
335}
336
337impl StreamerSendStats {
338 fn report_stats(
339 name: &'static str,
340 host_map: HashMap<IpAddr, SendStats>,
341 sample_duration: Option<Duration>,
342 ) {
343 const MAX_REPORT_ENTRIES: usize = 5;
344 let sample_ms = sample_duration.map(|d| d.as_millis()).unwrap_or_default();
345 let mut hist = Histogram::default();
346 let mut byte_sum = 0;
347 let mut pkt_count = 0;
348 host_map.iter().for_each(|(_addr, host_stats)| {
349 hist.increment(host_stats.bytes).unwrap();
350 byte_sum += host_stats.bytes;
351 pkt_count += host_stats.count;
352 });
353
354 datapoint_info!(
355 name,
356 ("streamer-send-sample_duration_ms", sample_ms, i64),
357 ("streamer-send-host_count", host_map.len(), i64),
358 ("streamer-send-bytes_total", byte_sum, i64),
359 ("streamer-send-pkt_count_total", pkt_count, i64),
360 (
361 "streamer-send-host_bytes_min",
362 hist.minimum().unwrap_or_default(),
363 i64
364 ),
365 (
366 "streamer-send-host_bytes_max",
367 hist.maximum().unwrap_or_default(),
368 i64
369 ),
370 (
371 "streamer-send-host_bytes_mean",
372 hist.mean().unwrap_or_default(),
373 i64
374 ),
375 (
376 "streamer-send-host_bytes_90pct",
377 hist.percentile(90.0).unwrap_or_default(),
378 i64
379 ),
380 (
381 "streamer-send-host_bytes_50pct",
382 hist.percentile(50.0).unwrap_or_default(),
383 i64
384 ),
385 (
386 "streamer-send-host_bytes_10pct",
387 hist.percentile(10.0).unwrap_or_default(),
388 i64
389 ),
390 );
391
392 let num_entries = host_map.len();
393 let mut entries: Vec<_> = host_map.into_iter().collect();
394 if entries.len() > MAX_REPORT_ENTRIES {
395 entries.select_nth_unstable_by_key(MAX_REPORT_ENTRIES, |(_addr, stats)| {
396 Reverse(stats.bytes)
397 });
398 entries.truncate(MAX_REPORT_ENTRIES);
399 }
400 info!("streamer send {name} hosts: count:{num_entries} {entries:?}");
401 }
402
403 fn maybe_submit(&mut self, name: &'static str, sender: &Sender<Box<dyn FnOnce() + Send>>) {
404 const SUBMIT_CADENCE: Duration = Duration::from_secs(10);
405 const MAP_SIZE_REPORTING_THRESHOLD: usize = 1_000;
406 let elapsed = self.since.as_ref().map(Instant::elapsed);
407 if elapsed.map(|e| e < SUBMIT_CADENCE).unwrap_or_default()
408 && self.host_map.len() < MAP_SIZE_REPORTING_THRESHOLD
409 {
410 return;
411 }
412
413 let host_map = std::mem::take(&mut self.host_map);
414 let _ = sender.send(Box::new(move || {
415 Self::report_stats(name, host_map, elapsed);
416 }));
417
418 *self = Self {
419 since: Some(Instant::now()),
420 ..Self::default()
421 };
422 }
423
424 fn record(&mut self, pkt: PacketRef) {
425 let ent = self.host_map.entry(pkt.meta().addr).or_default();
426 ent.count += 1;
427 ent.bytes += pkt.data(..).map(<[u8]>::len).unwrap_or_default() as u64;
428 }
429}
430
431impl StakedNodes {
432 fn calculate_stake_stats(
434 stakes: &Arc<HashMap<Pubkey, u64>>,
435 overrides: &HashMap<Pubkey, u64>,
436 ) -> (u64, u64, u64) {
437 let values = stakes
438 .iter()
439 .filter(|(pubkey, _)| !overrides.contains_key(pubkey))
440 .map(|(_, &stake)| stake)
441 .chain(overrides.values().copied())
442 .filter(|&stake| stake > 0);
443 let total_stake = values.clone().sum();
444 let (min_stake, max_stake) = values.minmax().into_option().unwrap_or_default();
445 (total_stake, min_stake, max_stake)
446 }
447
448 pub fn new(stakes: Arc<HashMap<Pubkey, u64>>, overrides: HashMap<Pubkey, u64>) -> Self {
449 let (total_stake, min_stake, max_stake) = Self::calculate_stake_stats(&stakes, &overrides);
450 Self {
451 stakes,
452 overrides,
453 total_stake,
454 max_stake,
455 min_stake,
456 }
457 }
458
459 pub fn get_node_stake(&self, pubkey: &Pubkey) -> Option<u64> {
460 self.overrides
461 .get(pubkey)
462 .or_else(|| self.stakes.get(pubkey))
463 .filter(|&&stake| stake > 0)
464 .copied()
465 }
466
467 #[inline]
468 pub fn total_stake(&self) -> u64 {
469 self.total_stake
470 }
471
472 #[inline]
473 pub(super) fn min_stake(&self) -> u64 {
474 self.min_stake
475 }
476
477 #[inline]
478 pub(super) fn max_stake(&self) -> u64 {
479 self.max_stake
480 }
481
482 pub fn update_stake_map(&mut self, stakes: Arc<HashMap<Pubkey, u64>>) {
484 let (total_stake, min_stake, max_stake) =
485 Self::calculate_stake_stats(&stakes, &self.overrides);
486
487 self.total_stake = total_stake;
488 self.min_stake = min_stake;
489 self.max_stake = max_stake;
490 self.stakes = stakes;
491 }
492}
493
494fn recv_send(
495 sock: &UdpSocket,
496 r: &PacketBatchReceiver,
497 socket_addr_space: &SocketAddrSpace,
498 stats: &mut Option<StreamerSendStats>,
499) -> Result<()> {
500 let timer = Duration::new(1, 0);
501 let packet_batch = r.recv_timeout(timer)?;
502 if let Some(stats) = stats {
503 packet_batch.iter().for_each(|p| stats.record(p));
504 }
505 let packets = packet_batch.iter().filter_map(|pkt| {
506 let addr = pkt.meta().socket_addr();
507 let data = pkt.data(..)?;
508 socket_addr_space.check(&addr).then_some((data, addr))
509 });
510 batch_send(sock, packets.collect::<Vec<_>>())?;
511 Ok(())
512}
513
514pub fn recv_packet_batches(
515 recvr: &PacketBatchReceiver,
516) -> Result<(Vec<PacketBatch>, usize, Duration)> {
517 let recv_start = Instant::now();
518 let timer = Duration::new(1, 0);
519 let packet_batch = recvr.recv_timeout(timer)?;
520 trace!("got packets");
521 let mut num_packets = packet_batch.len();
522 let mut packet_batches = vec![packet_batch];
523 while let Ok(packet_batch) = recvr.try_recv() {
524 trace!("got more packets");
525 num_packets += packet_batch.len();
526 packet_batches.push(packet_batch);
527 }
528 let recv_duration = recv_start.elapsed();
529 trace!(
530 "packet batches len: {}, num packets: {}",
531 packet_batches.len(),
532 num_packets
533 );
534 Ok((packet_batches, num_packets, recv_duration))
535}
536
537pub fn responder_atomic(
538 name: &'static str,
539 sockets: Arc<[UdpSocket]>,
540 bind_ip_addrs: Arc<BindIpAddrs>,
541 r: PacketBatchReceiver,
542 socket_addr_space: SocketAddrSpace,
543 stats_reporter_sender: Option<Sender<Box<dyn FnOnce() + Send>>>,
544) -> JoinHandle<()> {
545 Builder::new()
546 .name(format!("solRspndr{name}"))
547 .spawn(move || {
548 responder_loop(
549 MultihomedSocketProvider::new(sockets, bind_ip_addrs),
550 name,
551 r,
552 socket_addr_space,
553 stats_reporter_sender,
554 );
555 })
556 .unwrap()
557}
558
559pub fn responder(
560 name: &'static str,
561 sock: Arc<UdpSocket>,
562 r: PacketBatchReceiver,
563 socket_addr_space: SocketAddrSpace,
564 stats_reporter_sender: Option<Sender<Box<dyn FnOnce() + Send>>>,
565) -> JoinHandle<()> {
566 Builder::new()
567 .name(format!("solRspndr{name}"))
568 .spawn(move || {
569 responder_loop(
570 FixedSocketProvider::new(sock),
571 name,
572 r,
573 socket_addr_space,
574 stats_reporter_sender,
575 );
576 })
577 .unwrap()
578}
579
580fn responder_loop<P: SocketProvider>(
581 provider: P,
582 name: &'static str,
583 r: PacketBatchReceiver,
584 socket_addr_space: SocketAddrSpace,
585 stats_reporter_sender: Option<Sender<Box<dyn FnOnce() + Send>>>,
586) {
587 let mut errors = 0;
588 let mut last_error = None;
589 let mut last_print = 0;
590 let mut stats = None;
591
592 if stats_reporter_sender.is_some() {
593 stats = Some(StreamerSendStats::default());
594 }
595
596 loop {
597 let sock = provider.current_socket_ref();
598 if let Err(e) = recv_send(sock, &r, &socket_addr_space, &mut stats) {
599 match e {
600 StreamerError::RecvTimeout(RecvTimeoutError::Disconnected) => break,
601 StreamerError::RecvTimeout(RecvTimeoutError::Timeout) => (),
602 _ => {
603 errors += 1;
604 last_error = Some(e);
605 }
606 }
607 }
608 let now = timestamp();
609 if now - last_print > 1000 && errors != 0 {
610 datapoint_info!(name, ("errors", errors, i64),);
611 info!("{name} last-error: {last_error:?} count: {errors}");
612 last_print = now;
613 errors = 0;
614 }
615 if let Some(ref stats_reporter_sender) = stats_reporter_sender {
616 if let Some(ref mut stats) = stats {
617 stats.maybe_submit(name, stats_reporter_sender);
618 }
619 }
620 }
621}
622
623#[cfg(test)]
624mod test {
625 use {
626 super::*,
627 crate::{
628 packet::{Packet, PinnedPacketBatch, PACKET_DATA_SIZE},
629 streamer::{receiver, responder},
630 },
631 crossbeam_channel::unbounded,
632 solana_net_utils::sockets::bind_to_localhost_unique,
633 solana_perf::recycler::Recycler,
634 std::{
635 io::{self, Write},
636 sync::{
637 atomic::{AtomicBool, Ordering},
638 Arc,
639 },
640 time::Duration,
641 },
642 };
643
644 fn get_packet_batches(r: PacketBatchReceiver, num_packets: &mut usize) {
645 for _ in 0..10 {
646 let packet_batch_res = r.recv_timeout(Duration::new(1, 0));
647 if packet_batch_res.is_err() {
648 continue;
649 }
650
651 *num_packets -= packet_batch_res.unwrap().len();
652
653 if *num_packets == 0 {
654 break;
655 }
656 }
657 }
658
659 #[test]
660 fn streamer_debug() {
661 write!(io::sink(), "{:?}", Packet::default()).unwrap();
662 write!(io::sink(), "{:?}", PinnedPacketBatch::default()).unwrap();
663 }
664 #[test]
665 fn streamer_send_test() {
666 let read = bind_to_localhost_unique().expect("should bind reader");
667 read.set_read_timeout(Some(SOCKET_READ_TIMEOUT)).unwrap();
668 let addr = read.local_addr().unwrap();
669 let send = bind_to_localhost_unique().expect("should bind sender");
670 let exit = Arc::new(AtomicBool::new(false));
671 let (s_reader, r_reader) = unbounded();
672 let stats = Arc::new(StreamerReceiveStats::new("test"));
673 let t_receiver = receiver(
674 "solRcvrTest".to_string(),
675 Arc::new(read),
676 exit.clone(),
677 s_reader,
678 Recycler::default(),
679 stats.clone(),
680 Some(Duration::from_millis(1)), true,
682 None,
683 false,
684 );
685 const NUM_PACKETS: usize = 5;
686 let t_responder = {
687 let (s_responder, r_responder) = unbounded();
688 let t_responder = responder(
689 "SendTest",
690 Arc::new(send),
691 r_responder,
692 SocketAddrSpace::Unspecified,
693 None,
694 );
695 let mut packet_batch = PinnedPacketBatch::default();
696 for i in 0..NUM_PACKETS {
697 let mut p = Packet::default();
698 {
699 p.buffer_mut()[0] = i as u8;
700 p.meta_mut().size = PACKET_DATA_SIZE;
701 p.meta_mut().set_socket_addr(&addr);
702 }
703 packet_batch.push(p);
704 }
705 let packet_batch = PacketBatch::from(packet_batch);
706 s_responder.send(packet_batch).expect("send");
707 t_responder
708 };
709
710 let mut packets_remaining = NUM_PACKETS;
711 get_packet_batches(r_reader, &mut packets_remaining);
712 assert_eq!(packets_remaining, 0);
713 exit.store(true, Ordering::Relaxed);
714 assert!(stats.packet_batches_count.load(Ordering::Relaxed) >= 1);
715 assert_eq!(stats.packets_count.load(Ordering::Relaxed), NUM_PACKETS);
716 assert_eq!(stats.full_packet_batches_count.load(Ordering::Relaxed), 0);
717 t_receiver.join().expect("join");
718 t_responder.join().expect("join");
719 }
720}