1use std::collections::{HashMap, HashSet};
17use std::sync::Arc;
18use std::sync::atomic::AtomicBool;
19
20use api::v1::flow::{
21 CreateRequest, DirtyWindowRequests, DropRequest, FlowRequest, FlowResponse, FlushFlow,
22 flow_request,
23};
24use api::v1::region::InsertRequests;
25use catalog::CatalogManager;
26use common_base::Plugins;
27use common_error::ext::BoxedError;
28use common_meta::ddl::create_flow::FlowType;
29use common_meta::error::Result as MetaResult;
30use common_meta::key::flow::FlowMetadataManager;
31use common_meta::key::flow::flow_state::FlowStat;
32use common_runtime::JoinHandle;
33use common_telemetry::{error, info, trace, warn};
34use datatypes::value::Value;
35use futures::TryStreamExt;
36use itertools::Itertools;
37use operator::utils::try_to_session_query_context;
38use session::context::QueryContextBuilder;
39use snafu::{IntoError, OptionExt, ResultExt, ensure};
40use store_api::storage::{RegionId, TableId};
41use tokio::sync::{Mutex, RwLock};
42
43use crate::adapter::{CreateFlowArgs, StreamingEngine};
44use crate::batching_mode::engine::BatchingEngine;
45use crate::engine::{FlowEngine, FlowStatProvider};
46use crate::error::{
47 CreateFlowSnafu, ExternalSnafu, FlowNotFoundSnafu, FlowNotRecoveredSnafu,
48 IllegalCheckTaskStateSnafu, InsertIntoFlowSnafu, InternalSnafu, JoinTaskSnafu, ListFlowsSnafu,
49 NoAvailableFrontendSnafu, SyncCheckTaskSnafu, UnexpectedSnafu, UnsupportedSnafu,
50};
51use crate::metrics::{METRIC_FLOW_ROWS, METRIC_FLOW_TASK_COUNT};
52use crate::repr::{self, DiffRow};
53use crate::utils::StateReportHandler;
54use crate::{Error, FlowId};
55
56pub type FlowDualEngineRef = Arc<FlowDualEngine>;
58
59pub struct FlowDualEngine {
64 streaming_engine: Arc<StreamingEngine>,
65 batching_engine: Arc<BatchingEngine>,
66 state_report_handler: RwLock<Option<StateReportHandler>>,
68 src_table2flow: RwLock<SrcTableToFlow>,
70 flow_metadata_manager: Arc<FlowMetadataManager>,
71 catalog_manager: Arc<dyn CatalogManager>,
72 check_task: tokio::sync::Mutex<Option<ConsistentCheckTask>>,
73 plugins: Plugins,
74 done_recovering: AtomicBool,
75}
76
77impl FlowDualEngine {
78 pub fn new(
79 streaming_engine: Arc<StreamingEngine>,
80 batching_engine: Arc<BatchingEngine>,
81 flow_metadata_manager: Arc<FlowMetadataManager>,
82 catalog_manager: Arc<dyn CatalogManager>,
83 plugins: Plugins,
84 ) -> Self {
85 Self {
86 streaming_engine,
87 batching_engine,
88 state_report_handler: Default::default(),
89 src_table2flow: RwLock::new(SrcTableToFlow::default()),
90 flow_metadata_manager,
91 catalog_manager,
92 check_task: Mutex::new(None),
93 plugins,
94 done_recovering: AtomicBool::new(false),
95 }
96 }
97
98 pub fn set_done_recovering(&self) {
101 info!("FlowDualEngine done recovering");
102 self.done_recovering
103 .store(true, std::sync::atomic::Ordering::Release);
104 }
105
106 pub fn is_recover_done(&self) -> bool {
108 self.done_recovering
109 .load(std::sync::atomic::Ordering::Acquire)
110 }
111
112 async fn wait_for_all_flow_recover(&self, waiting_req_cnt: usize) -> Result<(), Error> {
114 if self.is_recover_done() {
115 return Ok(());
116 }
117
118 warn!(
119 "FlowDualEngine is not done recovering, {} insert request waiting for recovery",
120 waiting_req_cnt
121 );
122 let mut retry = 0;
125 let max_retry = 3;
126 while retry < max_retry && !self.is_recover_done() {
127 warn!(
128 "FlowDualEngine is not done recovering, retry {} in 1s",
129 retry
130 );
131 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
132 retry += 1;
133 }
134 if retry == max_retry {
135 return FlowNotRecoveredSnafu.fail();
136 } else {
137 info!("FlowDualEngine is done recovering");
138 }
139 Ok(())
141 }
142
143 pub fn plugins(&self) -> &Plugins {
144 &self.plugins
145 }
146
147 pub fn is_distributed(&self) -> bool {
149 self.streaming_engine.node_id.is_some()
150 }
151
152 pub fn streaming_engine(&self) -> Arc<StreamingEngine> {
153 self.streaming_engine.clone()
154 }
155
156 pub fn batching_engine(&self) -> Arc<BatchingEngine> {
157 self.batching_engine.clone()
158 }
159
160 pub async fn set_state_report_handler(&self, handler: StateReportHandler) {
161 *self.state_report_handler.write().await = Some(handler);
162 }
163
164 pub async fn gen_state_report(&self) -> FlowStat {
165 let streaming = self.streaming_engine.flow_stat().await;
166 let batching = self.batching_engine.flow_stat().await;
167
168 let mut state_size = streaming.state_size;
169 state_size.extend(batching.state_size);
170
171 let mut last_exec_time_map = streaming.last_exec_time_map;
172 last_exec_time_map.extend(batching.last_exec_time_map);
173
174 FlowStat {
175 state_size,
176 last_exec_time_map,
177 }
178 }
179
180 pub async fn start_state_report_task(self: Arc<Self>) -> Option<JoinHandle<()>> {
184 let state_report_handler = self.state_report_handler.write().await.take();
185 if let Some(mut handler) = state_report_handler {
186 let zelf = self.clone();
187 let handler = common_runtime::spawn_global(async move {
188 while let Some(ret_handler) = handler.recv().await {
189 let state_report = zelf.gen_state_report().await;
190 ret_handler.send(state_report).unwrap_or_else(|err| {
191 common_telemetry::error!(err; "Send state report error");
192 });
193 }
194 });
195 Some(handler)
196 } else {
197 None
198 }
199 }
200
201 async fn wait_for_available_frontend(&self, timeout: std::time::Duration) -> Result<(), Error> {
205 if !self.is_distributed() {
206 return Ok(());
207 }
208 let frontend_client = self.batching_engine().frontend_client.clone();
209 let sleep_duration = std::time::Duration::from_millis(1_000);
210 let now = std::time::Instant::now();
211 loop {
212 let frontend_list = frontend_client.scan_for_frontend().await?;
213 if !frontend_list.is_empty() {
214 let fe_list = frontend_list
215 .iter()
216 .map(|peer| &peer.addr)
217 .collect::<Vec<_>>();
218 info!("Available frontend found: {:?}", fe_list);
219 return Ok(());
220 }
221 let elapsed = now.elapsed();
222 tokio::time::sleep(sleep_duration).await;
223 info!("Waiting for available frontend, elapsed={:?}", elapsed);
224 if elapsed >= timeout {
225 return NoAvailableFrontendSnafu {
226 timeout,
227 context: "No available frontend found in cluster info",
228 }
229 .fail();
230 }
231 }
232 }
233
234 async fn try_sync_with_check_task(
238 &self,
239 flow_id: FlowId,
240 allow_drop: bool,
241 ) -> Result<(), Error> {
242 info!("Try to sync with check task for flow {}", flow_id);
244 let mut retry = 0;
245 let max_retry = 10;
246 while retry < max_retry {
248 if let Some(task) = self.check_task.lock().await.as_ref() {
249 task.trigger(false, allow_drop).await?;
250 break;
251 }
252 retry += 1;
253 tokio::time::sleep(std::time::Duration::from_millis(500)).await;
254 }
255
256 if retry == max_retry {
257 error!(
258 "Can't sync with check task for flow {} with allow_drop={}",
259 flow_id, allow_drop
260 );
261 return SyncCheckTaskSnafu {
262 flow_id,
263 allow_drop,
264 }
265 .fail();
266 }
267 info!("Successfully sync with check task for flow {}", flow_id);
268
269 Ok(())
270 }
271
272 async fn check_flow_consistent(
275 &self,
276 allow_create: bool,
277 allow_drop: bool,
278 ) -> Result<(), Error> {
279 let nodeid = self.streaming_engine.node_id;
281 let should_exists: Vec<_> = if let Some(nodeid) = nodeid {
282 let to_be_recover = self
285 .flow_metadata_manager
286 .flownode_flow_manager()
287 .flows(nodeid.into())
288 .try_collect::<Vec<_>>()
289 .await
290 .context(ListFlowsSnafu {
291 id: Some(nodeid.into()),
292 })?;
293 to_be_recover.into_iter().map(|(id, _)| id).collect()
294 } else {
295 let all_catalogs = self
298 .catalog_manager
299 .catalog_names()
300 .await
301 .map_err(BoxedError::new)
302 .context(ExternalSnafu)?;
303 let mut all_flow_ids = vec![];
304 for catalog in all_catalogs {
305 let flows = self
306 .flow_metadata_manager
307 .flow_name_manager()
308 .flow_names(&catalog)
309 .await
310 .try_collect::<Vec<_>>()
311 .await
312 .map_err(BoxedError::new)
313 .context(ExternalSnafu)?;
314
315 all_flow_ids.extend(flows.into_iter().map(|(_, id)| id.flow_id()));
316 }
317 all_flow_ids
318 };
319 let should_exists = should_exists
320 .into_iter()
321 .map(|i| i as FlowId)
322 .collect::<HashSet<_>>();
323 let actual_exists = self.list_flows().await?.into_iter().collect::<HashSet<_>>();
324 let to_be_created = should_exists
325 .iter()
326 .filter(|id| !actual_exists.contains(id))
327 .collect::<Vec<_>>();
328 let to_be_dropped = actual_exists
329 .iter()
330 .filter(|id| !should_exists.contains(id))
331 .collect::<Vec<_>>();
332
333 if !to_be_created.is_empty() {
334 if allow_create {
335 info!(
336 "Recovering {} flows: {:?}",
337 to_be_created.len(),
338 to_be_created
339 );
340 let mut errors = vec![];
341 for flow_id in to_be_created.clone() {
342 let flow_id = *flow_id;
343 let info = self
344 .flow_metadata_manager
345 .flow_info_manager()
346 .get(flow_id as u32)
347 .await
348 .map_err(BoxedError::new)
349 .context(ExternalSnafu)?
350 .context(FlowNotFoundSnafu { id: flow_id })?;
351
352 let sink_table_name = [
353 info.sink_table_name().catalog_name.clone(),
354 info.sink_table_name().schema_name.clone(),
355 info.sink_table_name().table_name.clone(),
356 ];
357 let args = CreateFlowArgs {
358 flow_id,
359 sink_table_name,
360 source_table_ids: info.source_table_ids().to_vec(),
361 create_if_not_exists: true,
365 or_replace: true,
366 expire_after: info.expire_after(),
367 eval_interval: info.eval_interval(),
368 comment: Some(info.comment().clone()),
369 sql: info.raw_sql().clone(),
370 flow_options: info.options().clone(),
371 query_ctx: info
372 .query_context()
373 .clone()
374 .map(|ctx| {
375 try_to_session_query_context(ctx)
376 .map_err(BoxedError::new)
377 .context(ExternalSnafu)
378 })
379 .transpose()?
380 .or_else(|| {
383 Some(
384 QueryContextBuilder::default()
385 .current_catalog(info.catalog_name().clone())
386 .build(),
387 )
388 }),
389 };
390 if let Err(err) = self
391 .create_flow(args)
392 .await
393 .map_err(BoxedError::new)
394 .with_context(|_| CreateFlowSnafu {
395 sql: info.raw_sql().clone(),
396 })
397 {
398 errors.push((flow_id, err));
399 }
400 }
401 if errors.is_empty() {
402 info!("Recover flows successfully, flows: {:?}", to_be_created);
403 }
404
405 for (flow_id, err) in errors {
406 warn!("Failed to recreate flow {}, err={:#?}", flow_id, err);
407 }
408 } else {
409 warn!(
410 "Flows do not exist in flownode for node {:?}, flow_ids={:?}",
411 nodeid, to_be_created
412 );
413 }
414 }
415 if !to_be_dropped.is_empty() {
416 if allow_drop {
417 info!("Dropping flows: {:?}", to_be_dropped);
418 let mut errors = vec![];
419 for flow_id in to_be_dropped {
420 let flow_id = *flow_id;
421 if let Err(err) = self.remove_flow(flow_id).await {
422 errors.push((flow_id, err));
423 }
424 }
425 for (flow_id, err) in errors {
426 warn!("Failed to drop flow {}, err={:#?}", flow_id, err);
427 }
428 } else {
429 warn!(
430 "Flows do not exist in metadata for node {:?}, flow_ids={:?}",
431 nodeid, to_be_dropped
432 );
433 }
434 }
435 Ok(())
436 }
437
438 pub async fn start_flow_consistent_check_task(self: &Arc<Self>) -> Result<(), Error> {
440 let mut check_task = self.check_task.lock().await;
441 ensure!(
442 check_task.is_none(),
443 IllegalCheckTaskStateSnafu {
444 reason: "Flow consistent check task already exists",
445 }
446 );
447 let task = ConsistentCheckTask::start_check_task(self).await?;
448 *check_task = Some(task);
449 Ok(())
450 }
451
452 pub async fn stop_flow_consistent_check_task(&self) -> Result<(), Error> {
453 info!("Stopping flow consistent check task");
454 let mut check_task = self.check_task.lock().await;
455
456 ensure!(
457 check_task.is_some(),
458 IllegalCheckTaskStateSnafu {
459 reason: "Flow consistent check task does not exist",
460 }
461 );
462
463 check_task.take().unwrap().stop().await?;
464 info!("Stopped flow consistent check task");
465 Ok(())
466 }
467
468 pub async fn reconcile_flows_from_metadata(&self) -> Result<(), Error> {
470 self.check_flow_consistent(true, true).await
471 }
472
473 async fn flow_exist_in_metadata(&self, flow_id: FlowId) -> Result<bool, Error> {
475 self.flow_metadata_manager
476 .flow_info_manager()
477 .get(flow_id as u32)
478 .await
479 .map_err(BoxedError::new)
480 .context(ExternalSnafu)
481 .map(|info| info.is_some())
482 }
483}
484
485struct ConsistentCheckTask {
486 handle: JoinHandle<()>,
487 shutdown_tx: tokio::sync::mpsc::Sender<()>,
488 trigger_tx: tokio::sync::mpsc::Sender<(bool, bool, tokio::sync::oneshot::Sender<()>)>,
489}
490
491impl ConsistentCheckTask {
492 async fn start_check_task(engine: &Arc<FlowDualEngine>) -> Result<Self, Error> {
493 let engine = engine.clone();
494 let min_refresh_duration = engine
495 .batching_engine()
496 .batch_opts
497 .experimental_min_refresh_duration;
498 let frontend_scan_timeout = engine
499 .batching_engine()
500 .batch_opts
501 .experimental_frontend_scan_timeout;
502 let (tx, mut rx) = tokio::sync::mpsc::channel(1);
503 let (trigger_tx, mut trigger_rx) =
504 tokio::sync::mpsc::channel::<(bool, bool, tokio::sync::oneshot::Sender<()>)>(10);
505 let handle = common_runtime::spawn_global(async move {
506 if let Err(err) = engine
508 .wait_for_available_frontend(frontend_scan_timeout)
509 .await
510 {
511 warn!("No frontend is available yet:\n {err:?}");
512 }
513
514 let mut recover_retry = 0;
516 while let Err(err) = engine.check_flow_consistent(true, false).await {
517 recover_retry += 1;
518 error!(
519 "Failed to recover flows:\n {err:?}, retry {} in {}s",
520 recover_retry,
521 min_refresh_duration.as_secs()
522 );
523 tokio::time::sleep(min_refresh_duration).await;
524 }
525
526 engine.set_done_recovering();
527
528 let (mut allow_create, mut allow_drop) = (false, false);
530 let mut ret_signal: Option<tokio::sync::oneshot::Sender<()>> = None;
531 loop {
532 if let Err(err) = engine.check_flow_consistent(allow_create, allow_drop).await {
533 error!(err; "Failed to check flow consistent");
534 }
535 if let Some(done) = ret_signal.take() {
536 let _ = done.send(());
537 }
538 tokio::select! {
539 _ = rx.recv() => break,
540 incoming = trigger_rx.recv() => if let Some(incoming) = incoming {
541 (allow_create, allow_drop) = (incoming.0, incoming.1);
542 ret_signal = Some(incoming.2);
543 },
544 _ = tokio::time::sleep(std::time::Duration::from_secs(10)) => {
545 (allow_create, allow_drop) = (false, false);
546 },
547 }
548 }
549 });
550 Ok(ConsistentCheckTask {
551 handle,
552 shutdown_tx: tx,
553 trigger_tx,
554 })
555 }
556
557 async fn trigger(&self, allow_create: bool, allow_drop: bool) -> Result<(), Error> {
558 let (tx, rx) = tokio::sync::oneshot::channel();
559 self.trigger_tx
560 .send((allow_create, allow_drop, tx))
561 .await
562 .map_err(|_| {
563 IllegalCheckTaskStateSnafu {
564 reason: "Failed to send trigger signal",
565 }
566 .build()
567 })?;
568 rx.await.map_err(|_| {
569 IllegalCheckTaskStateSnafu {
570 reason: "Failed to receive trigger signal",
571 }
572 .build()
573 })?;
574 Ok(())
575 }
576
577 async fn stop(self) -> Result<(), Error> {
578 self.shutdown_tx.send(()).await.map_err(|_| {
579 IllegalCheckTaskStateSnafu {
580 reason: "Failed to send shutdown signal",
581 }
582 .build()
583 })?;
584 self.handle.abort();
586 Ok(())
587 }
588}
589
590#[derive(Default)]
591struct SrcTableToFlow {
592 stream: HashMap<TableId, HashSet<FlowId>>,
594 batch: HashMap<TableId, HashSet<FlowId>>,
596 flow_infos: HashMap<FlowId, (FlowType, Vec<TableId>)>,
598}
599
600impl SrcTableToFlow {
601 fn in_stream(&self, table_id: TableId) -> bool {
602 self.stream.contains_key(&table_id)
603 }
604 fn in_batch(&self, table_id: TableId) -> bool {
605 self.batch.contains_key(&table_id)
606 }
607 fn add_flow(&mut self, flow_id: FlowId, flow_type: FlowType, src_table_ids: Vec<TableId>) {
608 let mapping = match flow_type {
609 FlowType::Streaming => &mut self.stream,
610 FlowType::Batching => &mut self.batch,
611 };
612
613 for src_table in src_table_ids.clone() {
614 mapping
615 .entry(src_table)
616 .and_modify(|flows| {
617 flows.insert(flow_id);
618 })
619 .or_insert_with(|| {
620 let mut set = HashSet::new();
621 set.insert(flow_id);
622 set
623 });
624 }
625 self.flow_infos.insert(flow_id, (flow_type, src_table_ids));
626 }
627
628 fn remove_flow(&mut self, flow_id: FlowId) {
629 let mapping = match self.get_flow_type(flow_id) {
630 Some(FlowType::Streaming) => &mut self.stream,
631 Some(FlowType::Batching) => &mut self.batch,
632 None => return,
633 };
634 if let Some((_, src_table_ids)) = self.flow_infos.remove(&flow_id) {
635 for src_table in src_table_ids {
636 if let Some(flows) = mapping.get_mut(&src_table) {
637 flows.remove(&flow_id);
638 }
639 }
640 }
641 }
642
643 fn get_flow_type(&self, flow_id: FlowId) -> Option<FlowType> {
644 self.flow_infos
645 .get(&flow_id)
646 .map(|(flow_type, _)| flow_type)
647 .cloned()
648 }
649}
650
651impl FlowEngine for FlowDualEngine {
652 async fn create_flow(&self, args: CreateFlowArgs) -> Result<Option<FlowId>, Error> {
653 let flow_type = args
654 .flow_options
655 .get(FlowType::FLOW_TYPE_KEY)
656 .map(|s| s.as_str());
657
658 let flow_type = match flow_type {
659 Some(FlowType::BATCHING) => FlowType::Batching,
660 Some(FlowType::STREAMING) => FlowType::Streaming,
661 None => FlowType::Batching,
662 Some(flow_type) => {
663 return InternalSnafu {
664 reason: format!("Invalid flow type: {}", flow_type),
665 }
666 .fail();
667 }
668 };
669
670 let flow_id = args.flow_id;
671 let src_table_ids = args.source_table_ids.clone();
672
673 let res = match flow_type {
674 FlowType::Batching => self.batching_engine.create_flow(args).await,
675 FlowType::Streaming => self.streaming_engine.create_flow(args).await,
676 }?;
677
678 self.src_table2flow
679 .write()
680 .await
681 .add_flow(flow_id, flow_type, src_table_ids);
682
683 Ok(res)
684 }
685
686 async fn remove_flow(&self, flow_id: FlowId) -> Result<(), Error> {
687 let flow_type = self.src_table2flow.read().await.get_flow_type(flow_id);
688
689 match flow_type {
690 Some(FlowType::Batching) => self.batching_engine.remove_flow(flow_id).await,
691 Some(FlowType::Streaming) => self.streaming_engine.remove_flow(flow_id).await,
692 None => {
693 warn!(
697 "Flow {} is not exist in the underlying engine, but exist in metadata",
698 flow_id
699 );
700 self.try_sync_with_check_task(flow_id, true).await?;
701
702 Ok(())
703 }
704 }?;
705 self.src_table2flow.write().await.remove_flow(flow_id);
707 Ok(())
708 }
709
710 async fn flush_flow(&self, flow_id: FlowId) -> Result<usize, Error> {
711 self.try_sync_with_check_task(flow_id, false).await?;
713 let flow_type = self.src_table2flow.read().await.get_flow_type(flow_id);
714 match flow_type {
715 Some(FlowType::Batching) => self.batching_engine.flush_flow(flow_id).await,
716 Some(FlowType::Streaming) => self.streaming_engine.flush_flow(flow_id).await,
717 None => {
718 warn!(
719 "Currently flow={flow_id} doesn't exist in flownode, ignore flush_flow request"
720 );
721 Ok(0)
722 }
723 }
724 }
725
726 async fn flow_exist(&self, flow_id: FlowId) -> Result<bool, Error> {
727 let flow_type = self.src_table2flow.read().await.get_flow_type(flow_id);
728 match flow_type {
730 Some(FlowType::Batching) => self.batching_engine.flow_exist(flow_id).await,
731 Some(FlowType::Streaming) => self.streaming_engine.flow_exist(flow_id).await,
732 None => Ok(false),
733 }
734 }
735
736 async fn list_flows(&self) -> Result<impl IntoIterator<Item = FlowId>, Error> {
737 let stream_flows = self.streaming_engine.list_flows().await?;
738 let batch_flows = self.batching_engine.list_flows().await?;
739
740 Ok(stream_flows.into_iter().chain(batch_flows))
741 }
742
743 async fn handle_flow_inserts(
744 &self,
745 request: api::v1::region::InsertRequests,
746 ) -> Result<(), Error> {
747 self.wait_for_all_flow_recover(request.requests.len())
748 .await?;
749 let mut to_stream_engine = Vec::with_capacity(request.requests.len());
751 let mut to_batch_engine = request.requests;
752
753 let mut batching_row_cnt = 0;
754 let mut streaming_row_cnt = 0;
755
756 {
757 let src_table2flow = self.src_table2flow.read().await;
759 to_batch_engine.retain(|req| {
760 let region_id = RegionId::from(req.region_id);
761 let table_id = region_id.table_id();
762 let is_in_stream = src_table2flow.in_stream(table_id);
763 let is_in_batch = src_table2flow.in_batch(table_id);
764 if is_in_stream {
765 streaming_row_cnt += req.rows.as_ref().map(|rs| rs.rows.len()).unwrap_or(0);
766 to_stream_engine.push(req.clone());
767 }
768 if is_in_batch {
769 batching_row_cnt += req.rows.as_ref().map(|rs| rs.rows.len()).unwrap_or(0);
770 return true;
771 }
772 if !is_in_batch && !is_in_stream {
773 warn!("Table {} is not any flow's source table", table_id)
775 }
776 false
777 });
778 }
781
782 METRIC_FLOW_ROWS
783 .with_label_values(&["in-streaming"])
784 .inc_by(streaming_row_cnt as u64);
785
786 METRIC_FLOW_ROWS
787 .with_label_values(&["in-batching"])
788 .inc_by(batching_row_cnt as u64);
789
790 let streaming_engine = self.streaming_engine.clone();
791 let stream_handler: JoinHandle<Result<(), Error>> =
792 common_runtime::spawn_global(async move {
793 streaming_engine
794 .handle_flow_inserts(api::v1::region::InsertRequests {
795 requests: to_stream_engine,
796 })
797 .await?;
798 Ok(())
799 });
800 self.batching_engine
801 .handle_flow_inserts(api::v1::region::InsertRequests {
802 requests: to_batch_engine,
803 })
804 .await?;
805 stream_handler.await.context(JoinTaskSnafu)??;
806
807 Ok(())
808 }
809
810 async fn handle_mark_window_dirty(
811 &self,
812 req: api::v1::flow::DirtyWindowRequests,
813 ) -> Result<(), Error> {
814 self.batching_engine.handle_mark_window_dirty(req).await
815 }
816}
817
818#[async_trait::async_trait]
819impl common_meta::node_manager::Flownode for FlowDualEngine {
820 async fn handle(&self, request: FlowRequest) -> MetaResult<FlowResponse> {
821 let query_ctx = request
822 .header
823 .and_then(|h| h.query_context)
824 .map(|ctx| ctx.into());
825 match request.body {
826 Some(flow_request::Body::Create(CreateRequest {
827 flow_id: Some(task_id),
828 source_table_ids,
829 sink_table_name: Some(sink_table_name),
830 create_if_not_exists,
831 expire_after,
832 eval_interval,
833 comment,
834 sql,
835 flow_options,
836 or_replace,
837 })) => {
838 let source_table_ids = source_table_ids.into_iter().map(|id| id.id).collect_vec();
839 let sink_table_name = [
840 sink_table_name.catalog_name,
841 sink_table_name.schema_name,
842 sink_table_name.table_name,
843 ];
844 let expire_after = expire_after.map(|e| e.value);
845 let args = CreateFlowArgs {
846 flow_id: task_id.id as u64,
847 sink_table_name,
848 source_table_ids,
849 create_if_not_exists,
850 or_replace,
851 expire_after,
852 eval_interval: eval_interval.map(|e| e.seconds),
853 comment: Some(comment),
854 sql: sql.clone(),
855 flow_options,
856 query_ctx,
857 };
858 let ret = self
859 .create_flow(args)
860 .await
861 .map_err(BoxedError::new)
862 .with_context(|_| CreateFlowSnafu { sql: sql.clone() })
863 .map_err(to_meta_err(snafu::location!()))?;
864 METRIC_FLOW_TASK_COUNT.inc();
865 Ok(FlowResponse {
866 affected_flows: ret
867 .map(|id| greptime_proto::v1::FlowId { id: id as u32 })
868 .into_iter()
869 .collect_vec(),
870 ..Default::default()
871 })
872 }
873 Some(flow_request::Body::Drop(DropRequest {
874 flow_id: Some(flow_id),
875 })) => {
876 self.remove_flow(flow_id.id as u64)
877 .await
878 .map_err(to_meta_err(snafu::location!()))?;
879 METRIC_FLOW_TASK_COUNT.dec();
880 Ok(Default::default())
881 }
882 Some(flow_request::Body::Flush(FlushFlow {
883 flow_id: Some(flow_id),
884 })) => {
885 let row = self
886 .flush_flow(flow_id.id as u64)
887 .await
888 .map_err(to_meta_err(snafu::location!()))?;
889 Ok(FlowResponse {
890 affected_flows: vec![flow_id],
891 affected_rows: row as u64,
892 ..Default::default()
893 })
894 }
895 other => common_meta::error::InvalidFlowRequestBodySnafu { body: other }.fail(),
896 }
897 }
898
899 async fn handle_inserts(&self, request: InsertRequests) -> MetaResult<FlowResponse> {
900 FlowEngine::handle_flow_inserts(self, request)
901 .await
902 .map(|_| Default::default())
903 .map_err(to_meta_err(snafu::location!()))
904 }
905
906 async fn handle_mark_window_dirty(&self, req: DirtyWindowRequests) -> MetaResult<FlowResponse> {
907 self.batching_engine()
908 .handle_mark_dirty_time_window(req)
909 .await
910 .map(|_| FlowResponse::default())
911 .map_err(to_meta_err(snafu::location!()))
912 }
913}
914
915fn to_meta_err(
917 location: snafu::Location,
918) -> impl FnOnce(crate::error::Error) -> common_meta::error::Error {
919 move |err: crate::error::Error| -> common_meta::error::Error {
920 match err {
921 crate::error::Error::FlowNotFound { id, .. } => {
922 common_meta::error::Error::FlowNotFound {
923 flow_name: format!("flow_id={id}"),
924 location,
925 }
926 }
927 _ => common_meta::error::Error::External {
928 location,
929 source: BoxedError::new(err),
930 },
931 }
932 }
933}
934
935impl FlowEngine for StreamingEngine {
936 async fn create_flow(&self, args: CreateFlowArgs) -> Result<Option<FlowId>, Error> {
937 self.create_flow_inner(args).await
938 }
939
940 async fn remove_flow(&self, flow_id: FlowId) -> Result<(), Error> {
941 self.remove_flow_inner(flow_id).await
942 }
943
944 async fn flush_flow(&self, flow_id: FlowId) -> Result<usize, Error> {
945 self.flush_flow_inner(flow_id).await
946 }
947
948 async fn flow_exist(&self, flow_id: FlowId) -> Result<bool, Error> {
949 self.flow_exist_inner(flow_id).await
950 }
951
952 async fn list_flows(&self) -> Result<impl IntoIterator<Item = FlowId>, Error> {
953 Ok(self
954 .flow_err_collectors
955 .read()
956 .await
957 .keys()
958 .cloned()
959 .collect::<Vec<_>>())
960 }
961
962 async fn handle_flow_inserts(
963 &self,
964 request: api::v1::region::InsertRequests,
965 ) -> Result<(), Error> {
966 self.handle_inserts_inner(request).await
967 }
968
969 async fn handle_mark_window_dirty(
970 &self,
971 _req: api::v1::flow::DirtyWindowRequests,
972 ) -> Result<(), Error> {
973 UnsupportedSnafu {
974 reason: "handle_mark_window_dirty in streaming engine",
975 }
976 .fail()
977 }
978}
979
980#[derive(Debug, Clone)]
982enum FetchFromRow {
983 Idx(usize),
984 Default(Value),
985}
986
987impl FetchFromRow {
988 fn fetch(&self, row: &repr::Row) -> Value {
990 match self {
991 FetchFromRow::Idx(idx) => row.get(*idx).unwrap().clone(),
992 FetchFromRow::Default(v) => v.clone(),
993 }
994 }
995}
996
997impl StreamingEngine {
998 async fn handle_inserts_inner(
999 &self,
1000 request: InsertRequests,
1001 ) -> std::result::Result<(), Error> {
1002 let _flush_lock = self.flush_lock.try_read();
1006 for write_request in request.requests {
1007 let region_id = write_request.region_id;
1008 let table_id = RegionId::from(region_id).table_id();
1009
1010 let (insert_schema, rows_proto) = write_request
1011 .rows
1012 .map(|r| (r.schema, r.rows))
1013 .unwrap_or_default();
1014
1015 let now = self.tick_manager.tick();
1017
1018 let (table_types, fetch_order) = {
1019 let ctx = self.node_context.read().await;
1020
1021 let table_schema = ctx.table_source.table_from_id(&table_id).await?;
1023 let default_vals = table_schema
1024 .default_values
1025 .iter()
1026 .zip(table_schema.relation_desc.typ().column_types.iter())
1027 .map(|(v, ty)| {
1028 v.as_ref().and_then(|v| {
1029 match v.create_default(ty.scalar_type(), ty.nullable()) {
1030 Ok(v) => Some(v),
1031 Err(err) => {
1032 common_telemetry::error!(err; "Failed to create default value");
1033 None
1034 }
1035 }
1036 })
1037 })
1038 .collect_vec();
1039
1040 let table_types = table_schema
1041 .relation_desc
1042 .typ()
1043 .column_types
1044 .clone()
1045 .into_iter()
1046 .map(|t| t.scalar_type)
1047 .collect_vec();
1048 let table_col_names = table_schema.relation_desc.names;
1049 let table_col_names = table_col_names
1050 .iter().enumerate()
1051 .map(|(idx,name)| match name {
1052 Some(name) => Ok(name.clone()),
1053 None => InternalSnafu {
1054 reason: format!("Expect column {idx} of table id={table_id} to have name in table schema, found None"),
1055 }
1056 .fail(),
1057 })
1058 .collect::<Result<Vec<_>, _>>()?;
1059 let name_to_col = HashMap::<_, _>::from_iter(
1060 insert_schema
1061 .iter()
1062 .enumerate()
1063 .map(|(i, name)| (&name.column_name, i)),
1064 );
1065
1066 let fetch_order: Vec<FetchFromRow> = table_col_names
1067 .iter()
1068 .zip(default_vals)
1069 .map(|(col_name, col_default_val)| {
1070 name_to_col
1071 .get(col_name)
1072 .copied()
1073 .map(FetchFromRow::Idx)
1074 .or_else(|| col_default_val.clone().map(FetchFromRow::Default))
1075 .with_context(|| UnexpectedSnafu {
1076 reason: format!(
1077 "Column not found: {}, default_value: {:?}",
1078 col_name, col_default_val
1079 ),
1080 })
1081 })
1082 .try_collect()?;
1083
1084 trace!("Reordering columns: {:?}", fetch_order);
1085 (table_types, fetch_order)
1086 };
1087
1088 let rows: Vec<DiffRow> = rows_proto
1090 .into_iter()
1091 .map(|r| {
1092 let r = repr::Row::from(r);
1093 let reordered = fetch_order.iter().map(|i| i.fetch(&r)).collect_vec();
1094 repr::Row::new(reordered)
1095 })
1096 .map(|r| (r, now, 1))
1097 .collect_vec();
1098 if let Err(err) = self
1099 .handle_write_request(region_id.into(), rows, &table_types)
1100 .await
1101 {
1102 let err = BoxedError::new(err);
1103 let flow_ids = self
1104 .node_context
1105 .read()
1106 .await
1107 .get_flow_ids(table_id)
1108 .into_iter()
1109 .flatten()
1110 .cloned()
1111 .collect_vec();
1112 let err = InsertIntoFlowSnafu {
1113 region_id,
1114 flow_ids,
1115 }
1116 .into_error(err);
1117 common_telemetry::error!(err; "Failed to handle write request");
1118 return Err(err);
1119 }
1120 }
1121 Ok(())
1122 }
1123}