risingwave_stream/executor/source/
mod.rs1use std::collections::HashMap;
16use std::sync::Arc;
17use std::time::Duration;
18
19use await_tree::InstrumentAwait;
20use itertools::Itertools;
21use risingwave_common::array::StreamChunk;
22use risingwave_common::bail;
23use risingwave_common::row::Row;
24use risingwave_common_rate_limit::RateLimiter;
25use risingwave_connector::error::ConnectorError;
26use risingwave_connector::source::{
27 BoxSourceChunkStream, BoxSourceReaderEventStream, BoxStreamingFileSourceChunkStream,
28 SourceColumnDesc, SourceReaderEvent, SplitId,
29};
30use risingwave_pb::plan_common::AdditionalColumn;
31use risingwave_pb::plan_common::additional_column::ColumnType;
32pub use state_table_handler::*;
33
34mod executor_core;
35pub use executor_core::StreamSourceCore;
36
37mod reader_stream;
38
39mod source_executor;
40pub use source_executor::*;
41mod dummy_source_executor;
42pub use dummy_source_executor::*;
43mod source_backfill_executor;
44pub use source_backfill_executor::*;
45mod fs_list_executor;
46pub use fs_list_executor::*;
47mod fs_fetch_executor;
48pub use fs_fetch_executor::*;
49mod iceberg_list_executor;
50pub use iceberg_list_executor::*;
51mod iceberg_fetch_executor;
52pub use iceberg_fetch_executor::*;
53mod batch_source; pub use batch_source::*;
55mod source_backfill_state_table;
56pub(crate) use source_backfill_state_table::BackfillStateTableHandler;
57
58pub mod state_table_handler;
59use futures_async_stream::try_stream;
60use risingwave_common::util::retry::exponential_backoff;
61use tokio::sync::mpsc::UnboundedReceiver;
62use tokio_retry::strategy::jitter;
63
64use crate::common::rate_limit::rate_limited_pieces;
65use crate::executor::error::StreamExecutorError;
66use crate::executor::{Barrier, Message};
67
68#[try_stream(ok = Message, error = StreamExecutorError)]
70pub async fn barrier_to_message_stream(mut rx: UnboundedReceiver<Barrier>) {
71 while let Some(barrier) = rx.recv().instrument_await("receive_barrier").await {
72 yield Message::Barrier(barrier);
73 }
74 bail!("barrier reader closed unexpectedly");
75}
76
77pub fn get_split_offset_mapping_from_chunk(
78 chunk: &StreamChunk,
79 split_idx: usize,
80 offset_idx: usize,
81) -> Option<HashMap<SplitId, String>> {
82 let mut split_offset_mapping = HashMap::new();
83 for i in 0..chunk.capacity() {
85 let (_, row, _) = chunk.row_at(i);
86 let split_id = row.datum_at(split_idx).unwrap().into_utf8().into();
87 let offset = row.datum_at(offset_idx).unwrap().into_utf8();
88 split_offset_mapping.insert(split_id, offset.to_owned());
89 }
90 Some(split_offset_mapping)
91}
92
93pub fn get_split_offset_col_idx(
95 column_descs: &[SourceColumnDesc],
96) -> (Option<usize>, Option<usize>, Option<usize>) {
97 let mut split_idx = None;
98 let mut offset_idx = None;
99 let mut pulsar_message_id_idx = None;
100 for (idx, column) in column_descs.iter().enumerate() {
101 match column.additional_column {
102 AdditionalColumn {
103 column_type: Some(ColumnType::Partition(_) | ColumnType::Filename(_)),
104 } => {
105 split_idx = Some(idx);
106 }
107 AdditionalColumn {
108 column_type: Some(ColumnType::Offset(_)),
109 } => {
110 offset_idx = Some(idx);
111 }
112 AdditionalColumn {
113 column_type: Some(ColumnType::PulsarMessageIdData(_)),
114 } => {
115 pulsar_message_id_idx = Some(idx);
116 }
117 _ => (),
118 }
119 }
120 (split_idx, offset_idx, pulsar_message_id_idx)
121}
122
123pub fn prune_additional_cols(
124 chunk: &StreamChunk,
125 to_prune_indices: &[usize],
126 column_descs: &[SourceColumnDesc],
127) -> StreamChunk {
128 chunk.project(
129 &(0..chunk.dimension())
130 .filter(|&idx| !to_prune_indices.contains(&idx) || column_descs[idx].is_visible())
131 .collect_vec(),
132 )
133}
134
135#[try_stream(ok = StreamChunk, error = ConnectorError)]
136pub async fn apply_rate_limit(stream: BoxSourceChunkStream, rate_limit_rps: Option<u32>) {
137 if rate_limit_rps == Some(0) {
138 let future = futures::future::pending::<()>();
140 future.await;
141 unreachable!();
142 }
143
144 let limiter = RateLimiter::new(
145 rate_limit_rps
146 .inspect(|limit| tracing::info!(rate_limit = limit, "rate limit applied"))
147 .into(),
148 );
149
150 #[for_await]
151 for chunk in stream {
152 let chunk = chunk?;
153 yield process_chunk(chunk, rate_limit_rps, &limiter).await;
154 }
155}
156
157#[try_stream(ok = SourceReaderEvent, error = ConnectorError)]
158pub async fn apply_rate_limit_to_source_reader_event(
159 stream: BoxSourceReaderEventStream,
160 rate_limit_rps: Option<u32>,
161) {
162 if rate_limit_rps == Some(0) {
163 let future = futures::future::pending::<()>();
165 future.await;
166 unreachable!();
167 }
168
169 let limiter = RateLimiter::new(
170 rate_limit_rps
171 .inspect(|limit| tracing::info!(rate_limit = limit, "rate limit applied"))
172 .into(),
173 );
174
175 #[for_await]
176 for event in stream {
177 match event? {
178 SourceReaderEvent::DataChunk(chunk) => {
179 yield SourceReaderEvent::DataChunk(
180 process_chunk(chunk, rate_limit_rps, &limiter).await,
181 )
182 }
183 SourceReaderEvent::SplitProgress(progress) => {
184 yield SourceReaderEvent::SplitProgress(progress)
185 }
186 }
187 }
188}
189
190#[try_stream(ok = StreamChunk, error = ConnectorError)]
191pub async fn source_reader_event_to_chunk_stream(stream: BoxSourceReaderEventStream) {
192 #[for_await]
193 for event in stream {
194 match event? {
195 SourceReaderEvent::DataChunk(chunk) => yield chunk,
196 SourceReaderEvent::SplitProgress(_) => {}
197 }
198 }
199}
200
201#[try_stream(ok = Option<StreamChunk>, error = ConnectorError)]
204pub async fn apply_shared_rate_limit_to_file_source_reader(
205 stream: BoxStreamingFileSourceChunkStream,
206 limiter: Arc<RateLimiter>,
207) {
208 #[for_await]
209 for chunk in stream {
210 match chunk? {
211 Some(chunk) =>
212 {
213 #[for_await]
214 for piece in rate_limited_pieces(&limiter, chunk) {
215 yield Some(piece);
216 }
217 }
218 None => yield None,
219 }
220 }
221}
222
223#[try_stream(ok = Option<StreamChunk>, error = ConnectorError)]
224pub async fn apply_rate_limit_with_for_streaming_file_source_reader(
225 stream: BoxStreamingFileSourceChunkStream,
226 rate_limit_rps: Option<u32>,
227) {
228 if rate_limit_rps == Some(0) {
229 let future = futures::future::pending::<()>();
231 future.await;
232 unreachable!();
233 }
234
235 let limiter = RateLimiter::new(
236 rate_limit_rps
237 .inspect(|limit| tracing::info!(rate_limit = limit, "rate limit applied"))
238 .into(),
239 );
240
241 #[for_await]
242 for chunk in stream {
243 let chunk_option = chunk?;
244 match chunk_option {
245 Some(chunk) => {
246 let processed_chunk = process_chunk(chunk, rate_limit_rps, &limiter).await;
247 yield Some(processed_chunk);
248 }
249 None => yield None,
250 }
251 }
252}
253
254async fn process_chunk(
255 chunk: StreamChunk,
256 rate_limit_rps: Option<u32>,
257 limiter: &RateLimiter,
258) -> StreamChunk {
259 let chunk_size = chunk.capacity();
260
261 if rate_limit_rps.is_none() || chunk_size == 0 {
262 return chunk;
264 }
265
266 let limit = rate_limit_rps.unwrap() as u64;
267 let required_permits = chunk.rate_limit_permits();
268 if required_permits > limit {
269 tracing::error!(
271 chunk_size,
272 required_permits,
273 limit,
274 "unexpected large chunk size"
275 );
276 }
277
278 limiter.wait(required_permits).await;
279 chunk
280}
281
282pub fn get_infinite_backoff_strategy() -> impl Iterator<Item = Duration> {
283 const BASE_DELAY: Duration = Duration::from_secs(1);
284 const BACKOFF_FACTOR: u64 = 2;
285 const MAX_DELAY: Duration = Duration::from_secs(10);
286 exponential_backoff(BASE_DELAY, BACKOFF_FACTOR, MAX_DELAY).map(jitter)
287}