Skip to main content

atlas_streamer/
streamer.rs

1//! The `streamer` module defines a set of services for efficiently pulling data from UDP sockets.
2//!
3
4use {
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// Total stake and nodes => stake map
78#[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        // Non-unix implementation may block indefinitely due to its lack of polling support,
173        // so we set a read timeout to avoid blocking indefinitely.
174        #[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            // Check for exit signal, even if socket is busy
198            // (for instance the leader transaction socket)
199            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    /// Calculate the stake stats: return the new (total_stake, min_stake and max_stake) tuple
433    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    // Update the stake map given a new stakes map
483    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)), // coalesce
681            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}