risingwave_stream/executor/source/
reader_stream.rs1use std::collections::HashMap;
16
17use futures::StreamExt;
18use itertools::Itertools;
19use risingwave_common::array::StreamChunk;
20use risingwave_common::catalog::ColumnId;
21use risingwave_common::id::SourceId;
22use risingwave_common::metrics::GLOBAL_ERROR_METRICS;
23use risingwave_connector::parser::schema_change::SchemaChangeEnvelope;
24use risingwave_connector::source::reader::desc::SourceDesc;
25use risingwave_connector::source::{
26 BoxSourceReaderEventStream, CdcAutoSchemaChangeFailCallback, ConnectorState,
27 CreateSplitReaderResult, SourceContext, SourceCtrlOpts, SourceReaderEvent, SplitId, SplitImpl,
28 SplitMetaData, StreamChunkWithState,
29};
30use thiserror_ext::AsReport;
31use tokio::sync::{mpsc, oneshot};
32
33use super::{
34 apply_rate_limit_to_source_reader_event, get_infinite_backoff_strategy,
35 get_split_offset_col_idx,
36};
37use crate::common::rate_limit::limited_chunk_size;
38use crate::executor::prelude::*;
39
40type AutoSchemaChangeSetup = (
41 Option<mpsc::Sender<(SchemaChangeEnvelope, oneshot::Sender<()>)>>,
42 Option<CdcAutoSchemaChangeFailCallback>,
43);
44
45#[derive(Debug)]
46pub(crate) enum SourceReaderEventWithState {
47 Data(StreamChunkWithState),
48 Progress(HashMap<SplitId, SplitImpl>),
49}
50
51pub(crate) struct StreamReaderBuilder {
52 pub source_desc: SourceDesc,
53 pub rate_limit: Option<u32>,
54 pub source_id: SourceId,
55 pub source_name: String,
56 pub reader_stream: Option<BoxSourceReaderEventStream>,
57
58 pub is_auto_schema_change_enable: bool,
60 pub actor_ctx: ActorContextRef,
61}
62
63impl StreamReaderBuilder {
64 fn update_offsets_from_chunk(
65 latest_splits_info: &mut HashMap<SplitId, SplitImpl>,
66 chunk: &StreamChunk,
67 split_idx: usize,
68 offset_idx: usize,
69 ) {
70 for i in 0..chunk.capacity() {
72 let (_, row, _) = chunk.row_at(i);
73 let split = row.datum_at(split_idx).unwrap().into_utf8();
74 let offset = row.datum_at(offset_idx).unwrap().into_utf8();
75 latest_splits_info
76 .get_mut(&Arc::from(split.to_owned()))
77 .map(|split_impl| split_impl.update_in_place(offset.to_owned()));
78 }
79 }
80
81 fn apply_split_progress(
82 latest_splits_info: &mut HashMap<SplitId, SplitImpl>,
83 split_progress: HashMap<SplitId, String>,
84 ) {
85 for (split_id, offset) in split_progress {
86 if let Some(split_impl) = latest_splits_info.get_mut(&split_id) {
87 if let Err(e) = split_impl.update_in_place(offset) {
88 tracing::warn!(
89 error = %e.as_report(),
90 split_id = %split_id,
91 "failed to apply split progress update",
92 );
93 }
94 } else {
95 tracing::warn!(
96 split_id = %split_id,
97 "ignore progress for unknown split",
98 );
99 }
100 }
101 }
102
103 fn setup_auto_schema_change(&self) -> AutoSchemaChangeSetup {
104 if self.is_auto_schema_change_enable {
105 let (schema_change_tx, mut schema_change_rx) =
106 mpsc::channel::<(SchemaChangeEnvelope, oneshot::Sender<()>)>(16);
107 let meta_client = self.actor_ctx.meta_client.clone();
108 let _join_handle = tokio::task::spawn(async move {
110 while let Some((schema_change, finish_tx)) = schema_change_rx.recv().await {
111 let table_ids = schema_change.table_ids();
112 tracing::info!(
113 target: "auto_schema_change",
114 "recv a schema change event for tables: {:?}", table_ids);
115 if let Some(ref meta_client) = meta_client {
117 match meta_client
118 .auto_schema_change(schema_change.to_protobuf())
119 .await
120 {
121 Ok(_) => {
122 tracing::info!(
123 target: "auto_schema_change",
124 "schema change success for tables: {:?}", table_ids);
125 let _ = finish_tx.send(());
126 }
127 Err(e) => {
128 tracing::error!(
129 target: "auto_schema_change",
130 error = %e.as_report(), "schema change error");
131
132 let _ = finish_tx.send(());
133 }
134 }
135 }
136 }
137 });
138
139 let on_cdc_auto_schema_change_failure = if let Some(ref meta_client) =
141 self.actor_ctx.meta_client
142 {
143 let meta_client = meta_client.clone();
144 Some(CdcAutoSchemaChangeFailCallback::new(
145 move |source_id: SourceId,
146 table_name: String,
147 cdc_table_id: String,
148 upstream_ddl: String,
149 fail_info: String| {
150 let meta_client = meta_client.clone();
151 tokio::spawn(async move {
152 if let Err(e) = meta_client
153 .add_cdc_auto_schema_change_fail_event(
154 source_id,
155 table_name,
156 cdc_table_id,
157 upstream_ddl,
158 fail_info,
159 )
160 .await
161 {
162 tracing::warn!(
163 error = %e.as_report(),
164 %source_id,
165 "Failed to add CDC auto schema change fail event to event log."
166 );
167 }
168 });
169 },
170 ))
171 } else {
172 None
173 };
174
175 (Some(schema_change_tx), on_cdc_auto_schema_change_failure)
176 } else {
177 info!("auto schema change is disabled in config");
178 (None, None)
179 }
180 }
181
182 fn prepare_source_stream_build(&self) -> (Vec<ColumnId>, SourceContext) {
183 let column_ids = self
184 .source_desc
185 .columns
186 .iter()
187 .map(|column_desc| column_desc.column_id)
188 .collect_vec();
189 debug_assert!(column_ids.iter().all_unique(), "column_ids must be unique");
190
191 let (schema_change_tx, on_cdc_auto_schema_change_failure) = self.setup_auto_schema_change();
192
193 let source_ctx = SourceContext::new_with_auto_schema_change_callback(
194 self.actor_ctx.id,
195 self.source_id,
196 self.actor_ctx.fragment_id,
197 self.source_name.clone(),
198 self.source_desc.metrics.clone(),
199 SourceCtrlOpts {
200 chunk_size: limited_chunk_size(self.rate_limit),
201 split_txn: self.rate_limit.is_some(), },
203 self.source_desc.source.config.clone(),
204 schema_change_tx,
205 on_cdc_auto_schema_change_failure,
206 );
207
208 (column_ids, source_ctx)
209 }
210
211 pub(crate) async fn fetch_latest_splits(
212 &mut self,
213 state: ConnectorState,
214 seek_to_latest: bool,
215 ) -> StreamExecutorResult<CreateSplitReaderResult> {
216 let (column_ids, source_ctx) = self.prepare_source_stream_build();
217 let source_ctx_ref = Arc::new(source_ctx);
218 let (stream, res) = self
219 .source_desc
220 .source
221 .build_stream(
222 state.clone(),
223 column_ids.clone(),
224 source_ctx_ref.clone(),
225 seek_to_latest,
226 )
227 .await
228 .map_err(StreamExecutorError::connector_error)?;
229 self.reader_stream = Some(stream);
230 Ok(res)
231 }
232
233 #[try_stream(ok = SourceReaderEventWithState, error = StreamExecutorError)]
234 pub(crate) async fn into_retry_stream(mut self, state: ConnectorState, is_initial_build: bool) {
235 let (column_ids, source_ctx) = self.prepare_source_stream_build();
236 let source_ctx_ref = Arc::new(source_ctx);
237
238 let mut latest_splits_info = {
239 if let Some(splits) = state.as_ref() {
240 splits
241 .iter()
242 .map(|split| (split.id(), split.clone()))
243 .collect::<HashMap<_, _>>()
244 } else {
245 HashMap::new()
246 }
247 };
248
249 let (Some(split_idx), Some(offset_idx), _) =
250 get_split_offset_col_idx(&self.source_desc.columns)
251 else {
252 unreachable!("Partition and offset columns must be set.");
253 };
254
255 let mut backoff = get_infinite_backoff_strategy();
256
257 'build_consume_loop: loop {
258 let bootstrap_state = if latest_splits_info.is_empty() {
259 None
260 } else {
261 Some(latest_splits_info.values().cloned().collect_vec())
262 };
263 tracing::debug!(
264 "build stream source reader with state: {:?}",
265 bootstrap_state
266 );
267 let build_stream_result = if let Some(exist_stream) = self.reader_stream.take() {
268 Ok((exist_stream, CreateSplitReaderResult::default()))
269 } else {
270 self.source_desc
271 .source
272 .build_stream(
273 bootstrap_state,
274 column_ids.clone(),
275 source_ctx_ref.clone(),
276 is_initial_build,
278 )
279 .await
280 };
281 if let Err(e) = build_stream_result {
282 if is_initial_build {
283 return Err(StreamExecutorError::connector_error(e));
284 } else {
285 let delay = backoff.next().unwrap();
286 tracing::error!(
287 error = %e.as_report(),
288 source_name = self.source_name,
289 source_id = %self.source_id,
290 actor_id = %self.actor_ctx.id,
291 "build stream source reader error, retry in {delay:?}"
292 );
293 GLOBAL_ERROR_METRICS.user_source_error.report([
294 e.variant_name().to_owned(),
295 self.source_id.to_string(),
296 self.source_name.clone(),
297 self.actor_ctx.fragment_id.to_string(),
298 ]);
299 tokio::time::sleep(delay).await;
300 continue 'build_consume_loop;
301 }
302 }
303
304 let (stream, _) = build_stream_result.unwrap();
305 let stream = apply_rate_limit_to_source_reader_event(stream, self.rate_limit).boxed();
306 let mut is_error = false;
307 let mut has_progress = false;
308 #[for_await]
309 'consume: for event in stream {
310 let event = match event {
311 Ok(event) => {
312 has_progress = true;
313 event
314 }
315 Err(e) => {
316 tracing::error!(
317 error = %e.as_report(),
318 source_name = self.source_name,
319 source_id = %self.source_id,
320 actor_id = %self.actor_ctx.id,
321 "stream source reader error"
322 );
323 GLOBAL_ERROR_METRICS.user_source_error.report([
324 e.variant_name().to_owned(),
325 self.source_id.to_string(),
326 self.source_name.clone(),
327 self.actor_ctx.fragment_id.to_string(),
328 ]);
329 is_error = true;
330 break 'consume;
331 }
332 };
333
334 match event {
335 SourceReaderEvent::DataChunk(chunk) => {
336 Self::update_offsets_from_chunk(
337 &mut latest_splits_info,
338 &chunk,
339 split_idx,
340 offset_idx,
341 );
342 yield SourceReaderEventWithState::Data((chunk, latest_splits_info.clone()));
343 }
344 SourceReaderEvent::SplitProgress(split_progress) => {
345 Self::apply_split_progress(&mut latest_splits_info, split_progress);
346 yield SourceReaderEventWithState::Progress(latest_splits_info.clone());
347 }
348 }
349 }
350 if !is_error {
351 tracing::info!("stream source reader consume finished");
352 latest_splits_info.values_mut().for_each(|split_impl| {
353 if let Some(mut batch_split) = split_impl.clone().into_batch_split() {
354 batch_split.finish();
355 *split_impl = batch_split.into();
356 }
357 });
358 yield SourceReaderEventWithState::Data((
359 StreamChunk::empty(
360 self.source_desc
361 .columns
362 .iter()
363 .map(|c| c.data_type.clone())
364 .collect_vec()
365 .as_slice(),
366 ),
367 latest_splits_info.clone(),
368 ));
369 break 'build_consume_loop;
370 }
371 if has_progress {
372 backoff = get_infinite_backoff_strategy();
373 }
374 let delay = backoff.next().unwrap();
375 tracing::info!("stream source reader error, retry in {delay:?}");
376 tokio::time::sleep(delay).await;
377 }
378 }
379}