catalog/system_schema/information_schema/
flow_statistics.rs1use std::sync::{Arc, Weak};
16
17use common_catalog::consts::INFORMATION_SCHEMA_FLOW_STATISTICS_TABLE_ID;
18use common_error::ext::BoxedError;
19use common_meta::key::FlowId;
20use common_meta::key::flow::FlowMetadataManager;
21use common_meta::key::flow::flow_state::FlowStat;
22use common_recordbatch::adapter::RecordBatchStreamAdapter;
23use common_recordbatch::{DfSendableRecordBatchStream, RecordBatch, SendableRecordBatchStream};
24use common_time::util::current_time_millis;
25use datafusion::execution::TaskContext;
26use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
27use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
28use datatypes::prelude::ConcreteDataType as CDT;
29use datatypes::scalars::ScalarVectorBuilder;
30use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
31use datatypes::timestamp::TimestampMillisecond;
32use datatypes::value::Value;
33use datatypes::vectors::{
34 Int64VectorBuilder, StringVectorBuilder, TimestampMillisecondVectorBuilder,
35 UInt32VectorBuilder, UInt64VectorBuilder, VectorRef,
36};
37use futures::TryStreamExt;
38use snafu::ResultExt;
39use store_api::storage::{ScanRequest, TableId};
40
41use crate::CatalogManager;
42use crate::error::{CreateRecordBatchSnafu, InternalSnafu, ListFlowsSnafu, Result};
43use crate::information_schema::{FLOW_STATISTICS, Predicates};
44use crate::system_schema::information_schema::InformationTable;
45use crate::system_schema::utils;
46
47const INIT_CAPACITY: usize = 42;
48
49pub const FLOW_ID: &str = "flow_id";
51pub const FLOW_NAME: &str = "flow_name";
52pub const START_TIME: &str = "start_time";
53pub const LAST_EXECUTION_TIME: &str = "last_execution_time";
54pub const UPTIME_SECONDS: &str = "uptime_seconds";
55pub const STATE_SIZE: &str = "state_size";
56
57#[derive(Debug)]
59pub(super) struct InformationSchemaFlowStatistics {
60 schema: SchemaRef,
61 catalog_name: String,
62 catalog_manager: Weak<dyn CatalogManager>,
63 flow_metadata_manager: Arc<FlowMetadataManager>,
64}
65
66impl InformationSchemaFlowStatistics {
67 pub(super) fn new(
68 catalog_name: String,
69 catalog_manager: Weak<dyn CatalogManager>,
70 flow_metadata_manager: Arc<FlowMetadataManager>,
71 ) -> Self {
72 Self {
73 schema: Self::schema(),
74 catalog_name,
75 catalog_manager,
76 flow_metadata_manager,
77 }
78 }
79
80 pub(crate) fn schema() -> SchemaRef {
81 Arc::new(Schema::new(
82 vec![
83 (FLOW_ID, CDT::uint32_datatype(), false),
84 (FLOW_NAME, CDT::string_datatype(), false),
85 (START_TIME, CDT::timestamp_millisecond_datatype(), true),
86 (
87 LAST_EXECUTION_TIME,
88 CDT::timestamp_millisecond_datatype(),
89 true,
90 ),
91 (UPTIME_SECONDS, CDT::int64_datatype(), true),
92 (STATE_SIZE, CDT::uint64_datatype(), true),
93 ]
94 .into_iter()
95 .map(|(name, ty, nullable)| ColumnSchema::new(name, ty, nullable))
96 .collect(),
97 ))
98 }
99
100 fn builder(&self) -> InformationSchemaFlowStatisticsBuilder {
101 InformationSchemaFlowStatisticsBuilder::new(
102 self.schema.clone(),
103 self.catalog_name.clone(),
104 self.catalog_manager.clone(),
105 &self.flow_metadata_manager,
106 )
107 }
108}
109
110impl InformationTable for InformationSchemaFlowStatistics {
111 fn table_id(&self) -> TableId {
112 INFORMATION_SCHEMA_FLOW_STATISTICS_TABLE_ID
113 }
114
115 fn table_name(&self) -> &'static str {
116 FLOW_STATISTICS
117 }
118
119 fn schema(&self) -> SchemaRef {
120 self.schema.clone()
121 }
122
123 fn to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream> {
124 let schema = self.schema.arrow_schema().clone();
125 let mut builder = self.builder();
126 let stream = Box::pin(DfRecordBatchStreamAdapter::new(
127 schema,
128 futures::stream::once(async move {
129 builder
130 .make_flow_statistics(Some(request))
131 .await
132 .map(|x| x.into_df_record_batch())
133 .map_err(|err| datafusion::error::DataFusionError::External(Box::new(err)))
134 }),
135 ));
136 Ok(Box::pin(
137 RecordBatchStreamAdapter::try_new(stream)
138 .map_err(BoxedError::new)
139 .context(InternalSnafu)?,
140 ))
141 }
142}
143
144struct InformationSchemaFlowStatisticsBuilder {
146 schema: SchemaRef,
147 catalog_name: String,
148 catalog_manager: Weak<dyn CatalogManager>,
149 flow_metadata_manager: Arc<FlowMetadataManager>,
150
151 flow_ids: UInt32VectorBuilder,
152 flow_names: StringVectorBuilder,
153 start_times: TimestampMillisecondVectorBuilder,
154 last_execution_times: TimestampMillisecondVectorBuilder,
155 uptime_seconds: Int64VectorBuilder,
156 state_sizes: UInt64VectorBuilder,
157}
158
159impl InformationSchemaFlowStatisticsBuilder {
160 fn new(
161 schema: SchemaRef,
162 catalog_name: String,
163 catalog_manager: Weak<dyn CatalogManager>,
164 flow_metadata_manager: &Arc<FlowMetadataManager>,
165 ) -> Self {
166 Self {
167 schema,
168 catalog_name,
169 catalog_manager,
170 flow_metadata_manager: flow_metadata_manager.clone(),
171
172 flow_ids: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
173 flow_names: StringVectorBuilder::with_capacity(INIT_CAPACITY),
174 start_times: TimestampMillisecondVectorBuilder::with_capacity(INIT_CAPACITY),
175 last_execution_times: TimestampMillisecondVectorBuilder::with_capacity(INIT_CAPACITY),
176 uptime_seconds: Int64VectorBuilder::with_capacity(INIT_CAPACITY),
177 state_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
178 }
179 }
180
181 async fn make_flow_statistics(&mut self, request: Option<ScanRequest>) -> Result<RecordBatch> {
183 let catalog_name = self.catalog_name.clone();
184 let predicates = Predicates::from_scan_request(&request);
185
186 let flow_info_manager = self.flow_metadata_manager.clone();
187
188 let mut stream = flow_info_manager
189 .flow_name_manager()
190 .flow_names(&catalog_name)
191 .await;
192
193 let flow_stat = {
194 let information_extension = utils::information_extension(&self.catalog_manager)?;
195 information_extension.flow_stats().await?
196 };
197
198 let now = current_time_millis();
199
200 while let Some((flow_name, flow_id)) = stream
201 .try_next()
202 .await
203 .map_err(BoxedError::new)
204 .context(ListFlowsSnafu {
205 catalog: &catalog_name,
206 })?
207 {
208 self.add_flow_statistic(&predicates, flow_id.flow_id(), &flow_name, &flow_stat, now);
209 }
210
211 self.finish()
212 }
213
214 fn add_flow_statistic(
215 &mut self,
216 predicates: &Predicates,
217 flow_id: FlowId,
218 flow_name: &str,
219 flow_stat: &Option<FlowStat>,
220 now: i64,
221 ) {
222 let row = [
223 (FLOW_ID, &Value::from(flow_id)),
224 (FLOW_NAME, &Value::from(flow_name.to_string())),
225 ];
226 if !predicates.eval(&row) {
227 return;
228 }
229
230 let start_time = flow_stat
231 .as_ref()
232 .and_then(|stat| stat.start_time_map.get(&flow_id).copied());
233
234 self.flow_ids.push(Some(flow_id));
235 self.flow_names.push(Some(flow_name));
236 self.start_times
237 .push(start_time.map(TimestampMillisecond::new));
238 self.last_execution_times
239 .push(flow_stat.as_ref().and_then(|stat| {
240 stat.last_exec_time_map
241 .get(&flow_id)
242 .map(|v| TimestampMillisecond::new(*v))
243 }));
244 self.uptime_seconds
245 .push(start_time.map(|start| ((now - start) / 1000).max(0)));
246 self.state_sizes.push(
247 flow_stat
248 .as_ref()
249 .and_then(|stat| stat.state_size.get(&flow_id).map(|v| *v as u64)),
250 );
251 }
252
253 fn finish(&mut self) -> Result<RecordBatch> {
254 let columns: Vec<VectorRef> = vec![
255 Arc::new(self.flow_ids.finish()),
256 Arc::new(self.flow_names.finish()),
257 Arc::new(self.start_times.finish()),
258 Arc::new(self.last_execution_times.finish()),
259 Arc::new(self.uptime_seconds.finish()),
260 Arc::new(self.state_sizes.finish()),
261 ];
262 RecordBatch::new(self.schema.clone(), columns).context(CreateRecordBatchSnafu)
263 }
264}
265
266impl DfPartitionStream for InformationSchemaFlowStatistics {
267 fn schema(&self) -> &arrow_schema::SchemaRef {
268 self.schema.arrow_schema()
269 }
270
271 fn execute(&self, _: Arc<TaskContext>) -> DfSendableRecordBatchStream {
272 let schema: Arc<arrow_schema::Schema> = self.schema.arrow_schema().clone();
273 let mut builder = self.builder();
274 Box::pin(DfRecordBatchStreamAdapter::new(
275 schema,
276 futures::stream::once(async move {
277 builder
278 .make_flow_statistics(None)
279 .await
280 .map(|x| x.into_df_record_batch())
281 .map_err(Into::into)
282 }),
283 ))
284 }
285}