snarkos_node_tcp/protocols/
reading.rs1#[cfg(doc)]
17use crate::{Config, protocols::Handshake};
18use crate::{
19 Connection,
20 ConnectionSide,
21 P2P,
22 Tcp,
23 connections::DisconnectOrigin,
24 protocols::{DisconnectOnDrop, ProtocolHandler, ReturnableConnection},
25};
26
27use async_trait::async_trait;
28use bytes::BytesMut;
29use futures_util::StreamExt;
30use std::{
31 io,
32 net::SocketAddr,
33 time::{Duration, Instant},
34};
35use tokio::{
36 io::AsyncRead,
37 sync::{mpsc, oneshot},
38 time::timeout,
39};
40use tokio_util::codec::{Decoder, FramedRead};
41use tracing::*;
42
43#[async_trait]
51pub trait Reading: P2P
52where
53 Self: Clone + Send + Sync + 'static,
54{
55 fn message_queue_depth(&self) -> usize {
61 1024
62 }
63
64 const INITIAL_BUFFER_SIZE: usize = 1024 * 1024;
69
70 const IDLE_TIMEOUT: Duration = Duration::from_secs(150);
72
73 type Message: Send;
75
76 type Codec: Decoder<Item = Self::Message, Error = io::Error> + Send;
78
79 async fn enable_reading(&self) {
81 let (conn_sender, mut conn_receiver) = mpsc::channel(self.tcp().config().max_connections as usize);
82
83 let (tx_reading, rx_reading) = oneshot::channel();
85
86 let self_clone = self.clone();
88 let reading_task = tokio::spawn(async move {
89 trace!(parent: self_clone.tcp().span(), "spawned the Reading handler task");
90 tx_reading.send(()).unwrap(); while let Some(returnable_conn) = conn_receiver.recv().await {
94 self_clone.handle_new_connection(returnable_conn).await;
95 }
96 });
97 let _ = rx_reading.await;
98 self.tcp().tasks.lock().push(reading_task);
99
100 let hdl = Box::new(ProtocolHandler(conn_sender));
102 assert!(self.tcp().protocols.reading.set(hdl).is_ok(), "the Reading protocol was enabled more than once!");
103 }
104
105 fn codec(&self, addr: SocketAddr, side: ConnectionSide) -> Self::Codec;
108
109 async fn process_message(&self, source: SocketAddr, message: Self::Message) -> io::Result<()>;
111}
112
113#[async_trait]
115trait ReadingInternal: Reading {
116 async fn handle_new_connection(&self, (conn, conn_returner): ReturnableConnection);
118
119 fn map_codec<T: AsyncRead>(
121 &self,
122 framed: FramedRead<T, Self::Codec>,
123 conn: &Connection,
124 ) -> FramedRead<T, CountingCodec<Self::Codec>>;
125}
126
127#[async_trait]
128impl<R: Reading> ReadingInternal for R {
129 async fn handle_new_connection(&self, (mut conn, conn_returner): ReturnableConnection) {
130 let addr = conn.addr();
131 let codec = self.codec(addr, !conn.side());
132 let reader = conn.reader.take().expect("missing connection reader!");
133 let framed = FramedRead::new(reader, codec);
134 let mut framed = self.map_codec(framed, &conn);
135
136 let (tx_conn_ready, rx_conn_ready) = oneshot::channel();
138 conn.readiness_notifier = Some(tx_conn_ready);
139
140 if Self::INITIAL_BUFFER_SIZE != 0 {
141 framed.read_buffer_mut().reserve(Self::INITIAL_BUFFER_SIZE);
142 }
143
144 let (inbound_message_sender, mut inbound_message_receiver) =
145 mpsc::channel::<(R::Message, QueuedMessageGuard)>(self.message_queue_depth());
146
147 let (tx_processing, rx_processing) = oneshot::channel::<()>();
149
150 let self_clone = self.clone();
152 let conn_span = conn.span().clone();
153 let inbound_processing_task = tokio::spawn(Box::pin(async move {
154 let node = self_clone.tcp();
155 trace!(parent: &conn_span, "spawned a task for processing messages");
156 tx_processing.send(()).unwrap(); let _conn_cleanup = DisconnectOnDrop::new(node.clone(), addr, DisconnectOrigin::Reading);
160
161 while let Some((msg, _guard)) = inbound_message_receiver.recv().await {
162 if let Err(e) = self_clone.process_message(addr, msg).await {
163 error!(parent: &conn_span, "can't process a message: {e}");
164 node.known_peers().register_failure(addr.ip());
165 }
166 }
168 }));
169 let _ = rx_processing.await;
170 conn.tasks.push(inbound_processing_task);
171
172 let (tx_reader, rx_reader) = oneshot::channel::<()>();
174
175 let node = self.tcp().clone();
177 let conn_span = conn.span().clone();
178 let reader_task = tokio::spawn(Box::pin(async move {
179 trace!(parent: &conn_span, "spawned a task for reading messages");
180 tx_reader.send(()).unwrap(); let _ = rx_conn_ready.await;
185
186 let _conn_cleanup = DisconnectOnDrop::new(node.clone(), addr, DisconnectOrigin::Reading);
188
189 let mut dropped_count: usize = 0;
191 let mut last_drop_log = Instant::now();
192
193 loop {
194 let next_frame_future = framed.next();
195 let read_result = match timeout(Self::IDLE_TIMEOUT, next_frame_future).await {
196 Ok(res) => res, Err(_) => {
198 debug!(parent: &conn_span, "connection timed out due to inactivity");
199 break;
200 }
201 };
202 match read_result {
203 Some(Ok(msg)) => {
204 if let Err(e) = inbound_message_sender.try_send((msg, QueuedMessageGuard::new())) {
206 node.stats().register_failure();
207 match e {
208 mpsc::error::TrySendError::Full(_) => {
209 dropped_count += 1;
211 if last_drop_log.elapsed() >= Duration::from_secs(1) {
212 warn_about_dropped_messages(&conn_span, &mut dropped_count, &mut last_drop_log);
213 }
214 }
215 mpsc::error::TrySendError::Closed(_) => {
216 error!(parent: &conn_span, "inbound channel closed");
217 break;
218 }
219 }
220 } else if dropped_count != 0 {
221 warn_about_dropped_messages(&conn_span, &mut dropped_count, &mut last_drop_log);
222 debug!(parent: &conn_span, "the inbound queue is no longer saturated");
223 }
224 #[cfg(feature = "metrics")]
225 metrics::increment_gauge(metrics::tcp::TCP_TASKS, 1f64);
226 }
227 Some(Err(e)) => {
228 error!(parent: &conn_span, "can't read: {e}");
229 node.known_peers().register_failure(addr.ip());
230 if node.config().fatal_io_errors.contains(&e.kind()) {
231 break;
232 }
233 }
234 None => break, }
236 }
237 }));
238 let _ = rx_reader.await;
239 conn.tasks.push(reader_task);
240
241 if conn_returner.send(Ok(conn)).is_err() {
243 unreachable!("couldn't return a Connection to the Tcp");
244 }
245 }
246
247 fn map_codec<T: AsyncRead>(
248 &self,
249 framed: FramedRead<T, Self::Codec>,
250 conn: &Connection,
251 ) -> FramedRead<T, CountingCodec<Self::Codec>> {
252 framed.map_decoder(|codec| CountingCodec { codec, node: self.tcp().clone(), acc: 0, span: conn.span().clone() })
253 }
254}
255
256struct CountingCodec<D: Decoder> {
258 codec: D,
259 node: Tcp,
260 acc: usize,
261 span: Span,
262}
263
264impl<D: Decoder> Decoder for CountingCodec<D> {
265 type Error = D::Error;
266 type Item = D::Item;
267
268 fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
269 let initial_buf_len = src.len();
270 let ret = self.codec.decode(src)?;
271 let final_buf_len = src.len();
272 let consumed = initial_buf_len.saturating_sub(final_buf_len);
275 let read_len = consumed + self.acc;
276
277 if read_len != 0 {
278 trace!(parent: &self.span, "read {read_len}B");
279
280 if ret.is_some() {
281 self.acc = 0;
282 self.node.stats().register_received_message(read_len);
284 } else {
285 self.acc = read_len;
286 }
287 }
288
289 Ok(ret)
290 }
291}
292
293struct QueuedMessageGuard;
298
299impl QueuedMessageGuard {
300 fn new() -> Self {
301 #[cfg(feature = "metrics")]
302 metrics::increment_gauge(metrics::tcp::TCP_TASKS, 1f64);
303 Self
304 }
305}
306
307impl Drop for QueuedMessageGuard {
308 fn drop(&mut self) {
309 #[cfg(feature = "metrics")]
310 metrics::decrement_gauge(metrics::tcp::TCP_TASKS, 1f64);
311 }
312}
313
314fn warn_about_dropped_messages(span: &Span, dropped_count: &mut usize, last_drop_log: &mut Instant) {
316 warn!(
317 parent: span,
318 "dropped {dropped_count} messages due\
319 to inbound queue saturation",
320 );
321 *dropped_count = 0;
323 *last_drop_log = Instant::now();
324}