1use std::collections::BTreeSet;
18use std::sync::Arc;
19
20use catalog::CatalogManagerRef;
21use client::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
22use common_error::ext::BoxedError;
23use common_meta::key::flow::FlowMetadataManagerRef;
24use common_recordbatch::{RecordBatch, RecordBatches, SendableRecordBatchStream};
25use common_runtime::JoinHandle;
26use common_telemetry::error;
27use datatypes::value::Value;
28use futures::StreamExt;
29use query::parser::QueryLanguageParser;
30use session::context::QueryContextBuilder;
31use snafu::{OptionExt, ResultExt, ensure};
32use table::metadata::TableId;
33
34use crate::adapter::table_source::ManagedTableSource;
35use crate::adapter::{FlowId, FlowStreamingEngineRef, StreamingEngine};
36use crate::error::{FlowNotFoundSnafu, JoinTaskSnafu, UnexpectedSnafu};
37use crate::expr::error::ExternalSnafu;
38use crate::expr::utils::find_plan_time_window_expr_lower_bound;
39use crate::repr::RelationDesc;
40use crate::server::get_all_flow_ids;
41use crate::{Error, FrontendInvoker};
42
43impl StreamingEngine {
44 pub async fn create_and_start_refill_flow_tasks(
46 self: &FlowStreamingEngineRef,
47 flow_metadata_manager: &FlowMetadataManagerRef,
48 catalog_manager: &CatalogManagerRef,
49 ) -> Result<(), Error> {
50 let tasks = self
51 .create_refill_flow_tasks(flow_metadata_manager, catalog_manager)
52 .await?;
53 self.starting_refill_flows(tasks).await?;
54 Ok(())
55 }
56
57 pub async fn create_refill_flow_tasks(
59 &self,
60 flow_metadata_manager: &FlowMetadataManagerRef,
61 catalog_manager: &CatalogManagerRef,
62 ) -> Result<Vec<RefillTask>, Error> {
63 let nodeid = self.node_id.map(|c| c as u64);
64
65 let flow_ids = get_all_flow_ids(flow_metadata_manager, catalog_manager, nodeid).await?;
66 let mut refill_tasks = Vec::new();
67 'flow_id_loop: for flow_id in flow_ids {
68 let info = flow_metadata_manager
69 .flow_info_manager()
70 .get(flow_id)
71 .await
72 .map_err(BoxedError::new)
73 .context(ExternalSnafu)?
74 .context(FlowNotFoundSnafu { id: flow_id })?;
75
76 for src_table in info.source_table_ids() {
78 if !self.table_info_source.check_table_exist(src_table).await? {
80 error!(
81 "Source table id = {:?} not found while refill flow_id={}, consider re-create the flow if necessary",
82 src_table, flow_id
83 );
84 continue 'flow_id_loop;
85 }
86 }
87
88 let expire_after = info
89 .expire_after()
90 .map(super::expire_after_secs_to_millis)
91 .transpose()?;
92 let now = self.tick_manager.tick();
94 let plan = self
95 .node_context
96 .read()
97 .await
98 .get_flow_plan(&FlowId::from(flow_id))
99 .context(FlowNotFoundSnafu { id: flow_id })?;
100 let time_range = if let Some(expire_after) = expire_after {
101 let low_bound = common_time::Timestamp::new_millisecond(now - expire_after);
102 let real_low_bound = find_plan_time_window_expr_lower_bound(&plan, low_bound)?;
103 real_low_bound.map(|l| (l, common_time::Timestamp::new_millisecond(now)))
104 } else {
105 None
106 };
107
108 common_telemetry::debug!(
109 "Time range for refill flow_id={} is {:?}",
110 flow_id,
111 time_range
112 );
113
114 for src_table in info.source_table_ids() {
115 let time_index_col = self
116 .table_info_source
117 .get_time_index_column_from_table_id(*src_table)
118 .await?
119 .1;
120 let time_index_name = time_index_col.name;
121 let task = RefillTask::create(
122 flow_id as u64,
123 *src_table,
124 time_range,
125 &time_index_name,
126 &self.table_info_source,
127 )
128 .await?;
129 refill_tasks.push(task);
130 }
131 }
132 Ok(refill_tasks)
133 }
134
135 pub(crate) async fn starting_refill_flows(
137 self: &FlowStreamingEngineRef,
138 tasks: Vec<RefillTask>,
139 ) -> Result<(), Error> {
140 let frontend_invoker =
142 self.frontend_invoker
143 .read()
144 .await
145 .clone()
146 .context(UnexpectedSnafu {
147 reason: "frontend invoker is not set",
148 })?;
149
150 for mut task in tasks {
151 task.start_running(self.clone(), &frontend_invoker).await?;
152 self.refill_tasks
155 .write()
156 .await
157 .insert(task.data.flow_id, task);
158 }
159 Ok(())
160 }
161}
162
163pub struct RefillTask {
165 data: TaskData,
166 state: TaskState<()>,
167}
168
169#[derive(Clone)]
170struct TaskData {
171 flow_id: FlowId,
172 table_id: TableId,
173 table_schema: RelationDesc,
174}
175
176impl TaskData {
177 fn validate_schema(table_schema: &RelationDesc, rb: &RecordBatch) -> Result<(), Error> {
179 let rb_schema = &rb.schema;
180 ensure!(
181 rb_schema.column_schemas().len() == table_schema.len()?,
182 UnexpectedSnafu {
183 reason: format!(
184 "RecordBatch schema length does not match table schema length, {}!={}",
185 rb_schema.column_schemas().len(),
186 table_schema.len()?
187 )
188 }
189 );
190 for (i, rb_col) in rb_schema.column_schemas().iter().enumerate() {
191 let (rb_name, rb_ty) = (rb_col.name.as_str(), &rb_col.data_type);
192 let (table_name, table_ty) = (
193 table_schema.names[i].as_ref(),
194 &table_schema.typ().column_types[i].scalar_type,
195 );
196 ensure!(
197 Some(rb_name) == table_name.map(|c| c.as_str()),
198 UnexpectedSnafu {
199 reason: format!(
200 "Mismatch in column names: expected {:?}, found {}",
201 table_name, rb_name
202 )
203 }
204 );
205
206 ensure!(
207 rb_ty == table_ty,
208 UnexpectedSnafu {
209 reason: format!(
210 "Mismatch in column types for {}: expected {:?}, found {:?}",
211 rb_name, table_ty, rb_ty
212 )
213 }
214 );
215 }
216 Ok(())
217 }
218}
219
220enum TaskState<T> {
222 Prepared { sql: String },
224 Running {
226 handle: JoinHandle<Result<T, Error>>,
227 },
228 Finished { res: Result<T, Error> },
230}
231
232impl<T> TaskState<T> {
233 fn new(sql: String) -> Self {
234 Self::Prepared { sql }
235 }
236}
237
238mod test_send {
239 use std::collections::BTreeMap;
240
241 use tokio::sync::RwLock;
242
243 use super::*;
244 fn is_send<T: Send + Sync>() {}
245 fn foo() {
246 is_send::<TaskState<()>>();
247 is_send::<RefillTask>();
248 is_send::<BTreeMap<FlowId, RefillTask>>();
249 is_send::<RwLock<BTreeMap<FlowId, RefillTask>>>();
250 }
251}
252
253impl TaskState<()> {
254 async fn is_finished(&mut self) -> Result<bool, Error> {
256 match self {
257 Self::Finished { .. } => Ok(true),
258 Self::Running { handle } => Ok(if handle.is_finished() {
259 *self = Self::Finished {
260 res: handle.await.context(JoinTaskSnafu)?,
261 };
262 true
263 } else {
264 false
265 }),
266 _ => Ok(false),
267 }
268 }
269
270 fn start_running(
271 &mut self,
272 task_data: &TaskData,
273 manager: FlowStreamingEngineRef,
274 mut output_stream: SendableRecordBatchStream,
275 ) -> Result<(), Error> {
276 let data = (*task_data).clone();
277 let handle: JoinHandle<Result<(), Error>> = common_runtime::spawn_global(async move {
278 while let Some(rb) = output_stream.next().await {
279 let rb = match rb {
280 Ok(rb) => rb,
281 Err(err) => Err(BoxedError::new(err)).context(ExternalSnafu)?,
282 };
283 TaskData::validate_schema(&data.table_schema, &rb)?;
284
285 manager
287 .node_context
288 .read()
289 .await
290 .send_rb(data.table_id, rb)
291 .await?;
292 }
293 common_telemetry::info!(
294 "Refill successful for source table_id={}, flow_id={}",
295 data.table_id,
296 data.flow_id
297 );
298 Ok(())
299 });
300 *self = Self::Running { handle };
301
302 Ok(())
303 }
304}
305
306enum QueryStream {
308 Batches { batches: RecordBatches },
309 Stream { stream: SendableRecordBatchStream },
310}
311
312impl TryFrom<common_query::Output> for QueryStream {
313 type Error = Error;
314 fn try_from(value: common_query::Output) -> Result<Self, Self::Error> {
315 match value.data {
316 common_query::OutputData::Stream(stream) => Ok(QueryStream::Stream { stream }),
317 common_query::OutputData::RecordBatches(batches) => {
318 Ok(QueryStream::Batches { batches })
319 }
320 _ => UnexpectedSnafu {
321 reason: format!("Unexpected output data type: {:?}", value.data),
322 }
323 .fail(),
324 }
325 }
326}
327
328impl QueryStream {
329 fn try_into_stream(self) -> Result<SendableRecordBatchStream, Error> {
330 match self {
331 Self::Batches { batches } => Ok(batches.as_stream()),
332 Self::Stream { stream } => Ok(stream),
333 }
334 }
335}
336
337impl RefillTask {
338 pub async fn create(
340 flow_id: FlowId,
341 table_id: TableId,
342 time_range: Option<(common_time::Timestamp, common_time::Timestamp)>,
343 time_col_name: &str,
344 table_src: &ManagedTableSource,
345 ) -> Result<RefillTask, Error> {
346 let (table_name, table_schema) = table_src.get_table_name_schema(&table_id).await?;
347 let all_col_names: BTreeSet<_> = table_schema
348 .relation_desc
349 .iter_names()
350 .flatten()
351 .map(|s| s.as_str())
352 .collect();
353
354 if !all_col_names.contains(time_col_name) {
355 UnexpectedSnafu {
356 reason: format!(
357 "Can't find column {} in table {} while refill flow",
358 time_col_name,
359 table_name.join(".")
360 ),
361 }
362 .fail()?;
363 }
364
365 let sql = if let Some(time_range) = time_range {
366 format!(
367 "select * from {0} where {1} >= {2} and {1} < {3}",
368 table_name.join("."),
369 time_col_name,
370 Value::from(time_range.0),
371 Value::from(time_range.1),
372 )
373 } else {
374 format!("select * from {0}", table_name.join("."))
375 };
376
377 Ok(RefillTask {
378 data: TaskData {
379 flow_id,
380 table_id,
381 table_schema: table_schema.relation_desc,
382 },
383 state: TaskState::new(sql),
384 })
385 }
386
387 pub async fn start_running(
389 &mut self,
390 manager: FlowStreamingEngineRef,
391 invoker: &FrontendInvoker,
392 ) -> Result<(), Error> {
393 let TaskState::Prepared { sql } = &mut self.state else {
394 UnexpectedSnafu {
395 reason: "task is not prepared",
396 }
397 .fail()?
398 };
399
400 let query_ctx = Arc::new(
402 QueryContextBuilder::default()
403 .current_catalog(DEFAULT_CATALOG_NAME.to_string())
404 .current_schema(DEFAULT_SCHEMA_NAME.to_string())
405 .build(),
406 );
407
408 let stmt_exec = invoker.statement_executor();
409
410 let stmt = QueryLanguageParser::parse_sql(sql, &query_ctx)
411 .map_err(BoxedError::new)
412 .context(ExternalSnafu)?;
413 let plan = stmt_exec
414 .plan(&stmt, query_ctx.clone())
415 .await
416 .map_err(BoxedError::new)
417 .context(ExternalSnafu)?;
418
419 let output_data = stmt_exec
420 .exec_plan(plan, query_ctx)
421 .await
422 .map_err(BoxedError::new)
423 .context(ExternalSnafu)?;
424
425 let output_stream = QueryStream::try_from(output_data)?;
426 let output_stream = output_stream.try_into_stream()?;
427
428 self.state
429 .start_running(&self.data, manager, output_stream)?;
430 Ok(())
431 }
432
433 pub async fn is_finished(&mut self) -> Result<bool, Error> {
434 self.state.is_finished().await
435 }
436}