@@ -25,10 +25,15 @@ use crate::{
2525use async_trait:: async_trait;
2626use bytes:: BytesMut ;
2727use futures_util:: StreamExt ;
28- use std:: { io, net:: SocketAddr } ;
28+ use std:: {
29+ io,
30+ net:: SocketAddr ,
31+ time:: { Duration , Instant } ,
32+ } ;
2933use tokio:: {
3034 io:: AsyncRead ,
3135 sync:: { mpsc, oneshot} ,
36+ time:: timeout,
3237} ;
3338use tokio_util:: codec:: { Decoder , FramedRead } ;
3439use tracing:: * ;
6065 /// The default value is 1024KiB.
6166 const INITIAL_BUFFER_SIZE : usize = 1024 * 1024 ;
6267
68+ /// The maximum time the node will wait for a new message before considering the connection dead.
69+ const IDLE_TIMEOUT : Duration = Duration :: from_secs ( 150 ) ;
70+
6371 /// The final (deserialized) type of inbound messages.
6472 type Message : Send ;
6573
6876
6977 /// Prepares the node to receive messages.
7078 async fn enable_reading ( & self ) {
71- let ( conn_sender, mut conn_receiver) = mpsc:: unbounded_channel ( ) ;
79+ let ( conn_sender, mut conn_receiver) = mpsc:: channel ( self . tcp ( ) . config ( ) . max_connections as usize ) ;
7280
7381 // use a channel to know when the reading task is ready
7482 let ( tx_reading, rx_reading) = oneshot:: channel ( ) ;
@@ -168,27 +176,57 @@ impl<R: Reading> ReadingInternal for R {
168176 // this task gets aborted, so there is no need for a dedicated timeout
169177 let _ = rx_conn_ready. await ;
170178
171- while let Some ( bytes) = framed. next ( ) . await {
172- match bytes {
173- Ok ( msg) => {
179+ // dropped message log suppression helpers
180+ let mut dropped_count: usize = 0 ;
181+ let mut last_drop_log = Instant :: now ( ) ;
182+
183+ loop {
184+ let next_frame_future = framed. next ( ) ;
185+ let read_result = match timeout ( Self :: IDLE_TIMEOUT , next_frame_future) . await {
186+ Ok ( res) => res, // IO completed (success or error)
187+ Err ( _) => {
188+ debug ! ( parent: node. span( ) , "connection with {addr} timed out due to inactivity" ) ;
189+ break ;
190+ }
191+ } ;
192+ match read_result {
193+ Some ( Ok ( msg) ) => {
174194 // send the message for further processing
175195 if let Err ( e) = inbound_message_sender. try_send ( msg) {
176- error ! ( parent: node. span( ) , "can't process a message from {addr}: {e}" ) ;
177196 node. stats ( ) . register_failure ( ) ;
178- if matches ! ( e, mpsc:: error:: TrySendError :: Closed ( _) ) {
179- break ;
197+ match e {
198+ mpsc:: error:: TrySendError :: Full ( _) => {
199+ // avoid log flooding
200+ dropped_count += 1 ;
201+ if last_drop_log. elapsed ( ) >= Duration :: from_secs ( 1 ) {
202+ warn_about_dropped_messages (
203+ & node,
204+ addr,
205+ & mut dropped_count,
206+ & mut last_drop_log,
207+ ) ;
208+ }
209+ }
210+ mpsc:: error:: TrySendError :: Closed ( _) => {
211+ error ! ( parent: node. span( ) , "inbound channel closed for {addr}" ) ;
212+ break ;
213+ }
180214 }
215+ } else if dropped_count != 0 {
216+ warn_about_dropped_messages ( & node, addr, & mut dropped_count, & mut last_drop_log) ;
217+ debug ! ( parent: node. span( ) , "the inbound queue for {addr} is no longer saturated" ) ;
181218 }
182219 #[ cfg( feature = "metrics" ) ]
183220 metrics:: increment_gauge ( metrics:: tcp:: TCP_TASKS , 1f64 ) ;
184221 }
185- Err ( e) => {
222+ Some ( Err ( e) ) => {
186223 error ! ( parent: node. span( ) , "can't read from {addr}: {e}" ) ;
187224 node. known_peers ( ) . register_failure ( addr. ip ( ) ) ;
188225 if node. config ( ) . fatal_io_errors . contains ( & e. kind ( ) ) {
189226 break ;
190227 }
191228 }
229+ None => break , // end of stream
192230 }
193231 }
194232
@@ -245,3 +283,15 @@ impl<D: Decoder> Decoder for CountingCodec<D> {
245283 Ok ( ret)
246284 }
247285}
286+
287+ /// Warns that some messages were dropped and resets the related counters.
288+ fn warn_about_dropped_messages ( node : & Tcp , addr : SocketAddr , dropped_count : & mut usize , last_drop_log : & mut Instant ) {
289+ warn ! (
290+ parent: node. span( ) ,
291+ "dropped {dropped_count} messages from {addr} due\
292+ to inbound queue saturation",
293+ ) ;
294+ // reset counters
295+ * dropped_count = 0 ;
296+ * last_drop_log = Instant :: now ( ) ;
297+ }
0 commit comments