@@ -2,10 +2,15 @@ use std::io::Read;
22use anyhow:: anyhow;
33use apache_avro:: Schema ;
44use apache_avro:: types:: Record ;
5- use tokio:: sync:: mpsc:: { unbounded_channel , UnboundedReceiver } ;
5+ use tokio:: sync:: mpsc;
66use crate :: { avros, global, metrics} ;
77use crate :: archiver:: datakind:: DataKind ;
88
9+ /// How many deserialized records the reader may buffer before it blocks waiting
10+ /// for the consumer to catch up. Keeps memory bounded while still allowing the
11+ /// reader thread to stay ahead of the consumer.
12+ const READ_CHANNEL_CAPACITY : usize = 16 ;
13+
914/// Wraps a `Read` and reports bytes read to metrics as they flow through.
1015struct CountingReader < R > {
1116 inner : R ,
@@ -20,8 +25,8 @@ impl<R: Read> Read for CountingReader<R> {
2025 }
2126}
2227
23- pub ( super ) fn consume_sync < R : Read + Send + ' static > ( kind : DataKind , schema : & ' static Schema , reader : R ) -> UnboundedReceiver < Record < ' static > > {
24- let ( tx, rx) = unbounded_channel ( ) ;
28+ pub ( super ) fn consume_sync < R : Read + Send + ' static > ( kind : DataKind , schema : & ' static Schema , reader : R ) -> mpsc :: Receiver < Record < ' static > > {
29+ let ( tx, rx) = mpsc :: channel ( READ_CHANNEL_CAPACITY ) ;
2530
2631 tokio:: task:: spawn_blocking ( move || {
2732 tracing:: trace!( "Reading avro file..." ) ;
@@ -50,7 +55,9 @@ pub(super) fn consume_sync<R: Read + Send + 'static>(kind: DataKind, schema: &'s
5055 continue
5156 }
5257 let record = record. unwrap ( ) ;
53- if let Err ( e) = tx. send ( record) {
58+ // blocking_send applies backpressure — the reader thread pauses when
59+ // the channel is full, preventing unbounded memory growth
60+ if let Err ( e) = tx. blocking_send ( record) {
5461 tracing:: error!( "Error sending record to channel: {:?}" , e) ;
5562 break
5663 }
0 commit comments