1use std::sync::{Arc, Weak};
16
17use arrow_schema::SchemaRef as ArrowSchemaRef;
18use common_catalog::consts::INFORMATION_SCHEMA_REGION_STATISTICS_TABLE_ID;
19use common_error::ext::BoxedError;
20use common_meta::datanode::RegionStat;
21use common_recordbatch::adapter::RecordBatchStreamAdapter;
22use common_recordbatch::{DfSendableRecordBatchStream, RecordBatch, SendableRecordBatchStream};
23use datafusion::execution::TaskContext;
24use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
25use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
26use datatypes::prelude::{ConcreteDataType, ScalarVectorBuilder, VectorRef};
27use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
28use datatypes::value::Value;
29use datatypes::vectors::{StringVectorBuilder, UInt32VectorBuilder, UInt64VectorBuilder};
30use snafu::ResultExt;
31use store_api::storage::{ScanRequest, TableId};
32
33use crate::CatalogManager;
34use crate::error::{CreateRecordBatchSnafu, InternalSnafu, Result};
35use crate::information_schema::Predicates;
36use crate::system_schema::information_schema::{InformationTable, REGION_STATISTICS};
37use crate::system_schema::utils;
38
39const REGION_ID: &str = "region_id";
40const TABLE_ID: &str = "table_id";
41const REGION_NUMBER: &str = "region_number";
42const REGION_ROWS: &str = "region_rows";
43const WRITTEN_BYTES: &str = "written_bytes_since_open";
44const QUERY_CPU_TIME_MILLIS: &str = "query_cpu_time_millis";
45const QUERY_SCANNED_BYTES: &str = "query_scanned_bytes";
46const DISK_SIZE: &str = "disk_size";
47const MEMTABLE_SIZE: &str = "memtable_size";
48const MANIFEST_SIZE: &str = "manifest_size";
49const SST_SIZE: &str = "sst_size";
50const SST_NUM: &str = "sst_num";
51const INDEX_SIZE: &str = "index_size";
52const ENGINE: &str = "engine";
53const REGION_ROLE: &str = "region_role";
54
55const INIT_CAPACITY: usize = 42;
56
57#[derive(Debug)]
74pub(super) struct InformationSchemaRegionStatistics {
75 schema: SchemaRef,
76 catalog_manager: Weak<dyn CatalogManager>,
77}
78
79impl InformationSchemaRegionStatistics {
80 pub(super) fn new(catalog_manager: Weak<dyn CatalogManager>) -> Self {
81 Self {
82 schema: Self::schema(),
83 catalog_manager,
84 }
85 }
86
87 pub(crate) fn schema() -> SchemaRef {
88 Arc::new(Schema::new(vec![
89 ColumnSchema::new(REGION_ID, ConcreteDataType::uint64_datatype(), false),
90 ColumnSchema::new(TABLE_ID, ConcreteDataType::uint32_datatype(), false),
91 ColumnSchema::new(REGION_NUMBER, ConcreteDataType::uint32_datatype(), false),
92 ColumnSchema::new(REGION_ROWS, ConcreteDataType::uint64_datatype(), true),
93 ColumnSchema::new(WRITTEN_BYTES, ConcreteDataType::uint64_datatype(), true),
94 ColumnSchema::new(
95 QUERY_CPU_TIME_MILLIS,
96 ConcreteDataType::uint64_datatype(),
97 true,
98 ),
99 ColumnSchema::new(
100 QUERY_SCANNED_BYTES,
101 ConcreteDataType::uint64_datatype(),
102 true,
103 ),
104 ColumnSchema::new(DISK_SIZE, ConcreteDataType::uint64_datatype(), true),
105 ColumnSchema::new(MEMTABLE_SIZE, ConcreteDataType::uint64_datatype(), true),
106 ColumnSchema::new(MANIFEST_SIZE, ConcreteDataType::uint64_datatype(), true),
107 ColumnSchema::new(SST_SIZE, ConcreteDataType::uint64_datatype(), true),
108 ColumnSchema::new(SST_NUM, ConcreteDataType::uint64_datatype(), true),
109 ColumnSchema::new(INDEX_SIZE, ConcreteDataType::uint64_datatype(), true),
110 ColumnSchema::new(ENGINE, ConcreteDataType::string_datatype(), true),
111 ColumnSchema::new(REGION_ROLE, ConcreteDataType::string_datatype(), true),
112 ]))
113 }
114
115 fn builder(&self) -> InformationSchemaRegionStatisticsBuilder {
116 InformationSchemaRegionStatisticsBuilder::new(
117 self.schema.clone(),
118 self.catalog_manager.clone(),
119 )
120 }
121}
122
123impl InformationTable for InformationSchemaRegionStatistics {
124 fn table_id(&self) -> TableId {
125 INFORMATION_SCHEMA_REGION_STATISTICS_TABLE_ID
126 }
127
128 fn table_name(&self) -> &'static str {
129 REGION_STATISTICS
130 }
131
132 fn schema(&self) -> SchemaRef {
133 self.schema.clone()
134 }
135
136 fn to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream> {
137 let schema = self.schema.arrow_schema().clone();
138 let mut builder = self.builder();
139
140 let stream = Box::pin(DfRecordBatchStreamAdapter::new(
141 schema,
142 futures::stream::once(async move {
143 builder
144 .make_region_statistics(Some(request))
145 .await
146 .map(|x| x.into_df_record_batch())
147 .map_err(Into::into)
148 }),
149 ));
150
151 Ok(Box::pin(
152 RecordBatchStreamAdapter::try_new(stream)
153 .map_err(BoxedError::new)
154 .context(InternalSnafu)?,
155 ))
156 }
157}
158
159struct InformationSchemaRegionStatisticsBuilder {
160 schema: SchemaRef,
161 catalog_manager: Weak<dyn CatalogManager>,
162
163 region_ids: UInt64VectorBuilder,
164 table_ids: UInt32VectorBuilder,
165 region_numbers: UInt32VectorBuilder,
166 region_rows: UInt64VectorBuilder,
167 written_bytes: UInt64VectorBuilder,
168 query_cpu_time_millis: UInt64VectorBuilder,
169 query_scanned_bytes: UInt64VectorBuilder,
170 disk_sizes: UInt64VectorBuilder,
171 memtable_sizes: UInt64VectorBuilder,
172 manifest_sizes: UInt64VectorBuilder,
173 sst_sizes: UInt64VectorBuilder,
174 sst_nums: UInt64VectorBuilder,
175 index_sizes: UInt64VectorBuilder,
176 engines: StringVectorBuilder,
177 region_roles: StringVectorBuilder,
178}
179
180impl InformationSchemaRegionStatisticsBuilder {
181 fn new(schema: SchemaRef, catalog_manager: Weak<dyn CatalogManager>) -> Self {
182 Self {
183 schema,
184 catalog_manager,
185 region_ids: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
186 table_ids: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
187 region_numbers: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
188 region_rows: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
189 written_bytes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
190 query_cpu_time_millis: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
191 query_scanned_bytes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
192 disk_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
193 memtable_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
194 manifest_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
195 sst_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
196 sst_nums: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
197 index_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
198 engines: StringVectorBuilder::with_capacity(INIT_CAPACITY),
199 region_roles: StringVectorBuilder::with_capacity(INIT_CAPACITY),
200 }
201 }
202
203 async fn make_region_statistics(
205 &mut self,
206 request: Option<ScanRequest>,
207 ) -> Result<RecordBatch> {
208 let predicates = Predicates::from_scan_request(&request);
209 let information_extension = utils::information_extension(&self.catalog_manager)?;
210 let region_stats = information_extension.region_stats().await?;
211 for region_stat in region_stats {
212 self.add_region_statistic(&predicates, region_stat);
213 }
214 self.finish()
215 }
216
217 fn add_region_statistic(&mut self, predicate: &Predicates, region_stat: RegionStat) {
218 let row = [
219 (REGION_ID, &Value::from(region_stat.id.as_u64())),
220 (TABLE_ID, &Value::from(region_stat.id.table_id())),
221 (REGION_NUMBER, &Value::from(region_stat.id.region_number())),
222 (REGION_ROWS, &Value::from(region_stat.num_rows)),
223 (WRITTEN_BYTES, &Value::from(region_stat.written_bytes)),
224 (
225 QUERY_CPU_TIME_MILLIS,
226 &Value::from(region_stat.query_cpu_time / 1_000_000),
227 ),
228 (
229 QUERY_SCANNED_BYTES,
230 &Value::from(region_stat.query_scanned_bytes),
231 ),
232 (DISK_SIZE, &Value::from(region_stat.approximate_bytes)),
233 (MEMTABLE_SIZE, &Value::from(region_stat.memtable_size)),
234 (MANIFEST_SIZE, &Value::from(region_stat.manifest_size)),
235 (SST_SIZE, &Value::from(region_stat.sst_size)),
236 (SST_NUM, &Value::from(region_stat.sst_num)),
237 (INDEX_SIZE, &Value::from(region_stat.index_size)),
238 (ENGINE, &Value::from(region_stat.engine.as_str())),
239 (REGION_ROLE, &Value::from(region_stat.role.to_string())),
240 ];
241
242 if !predicate.eval(&row) {
243 return;
244 }
245
246 self.region_ids.push(Some(region_stat.id.as_u64()));
247 self.table_ids.push(Some(region_stat.id.table_id()));
248 self.region_numbers
249 .push(Some(region_stat.id.region_number()));
250 self.region_rows.push(Some(region_stat.num_rows));
251 self.written_bytes.push(Some(region_stat.written_bytes));
252 self.query_cpu_time_millis
253 .push(Some(region_stat.query_cpu_time / 1_000_000));
254 self.query_scanned_bytes
255 .push(Some(region_stat.query_scanned_bytes));
256 self.disk_sizes.push(Some(region_stat.approximate_bytes));
257 self.memtable_sizes.push(Some(region_stat.memtable_size));
258 self.manifest_sizes.push(Some(region_stat.manifest_size));
259 self.sst_sizes.push(Some(region_stat.sst_size));
260 self.sst_nums.push(Some(region_stat.sst_num));
261 self.index_sizes.push(Some(region_stat.index_size));
262 self.engines.push(Some(®ion_stat.engine));
263 self.region_roles.push(Some(®ion_stat.role.to_string()));
264 }
265
266 fn finish(&mut self) -> Result<RecordBatch> {
267 let columns: Vec<VectorRef> = vec![
268 Arc::new(self.region_ids.finish()),
269 Arc::new(self.table_ids.finish()),
270 Arc::new(self.region_numbers.finish()),
271 Arc::new(self.region_rows.finish()),
272 Arc::new(self.written_bytes.finish()),
273 Arc::new(self.query_cpu_time_millis.finish()),
274 Arc::new(self.query_scanned_bytes.finish()),
275 Arc::new(self.disk_sizes.finish()),
276 Arc::new(self.memtable_sizes.finish()),
277 Arc::new(self.manifest_sizes.finish()),
278 Arc::new(self.sst_sizes.finish()),
279 Arc::new(self.sst_nums.finish()),
280 Arc::new(self.index_sizes.finish()),
281 Arc::new(self.engines.finish()),
282 Arc::new(self.region_roles.finish()),
283 ];
284
285 RecordBatch::new(self.schema.clone(), columns).context(CreateRecordBatchSnafu)
286 }
287}
288
289impl DfPartitionStream for InformationSchemaRegionStatistics {
290 fn schema(&self) -> &ArrowSchemaRef {
291 self.schema.arrow_schema()
292 }
293
294 fn execute(&self, _: Arc<TaskContext>) -> DfSendableRecordBatchStream {
295 let schema = self.schema.arrow_schema().clone();
296 let mut builder = self.builder();
297 Box::pin(DfRecordBatchStreamAdapter::new(
298 schema,
299 futures::stream::once(async move {
300 builder
301 .make_region_statistics(None)
302 .await
303 .map(|x| x.into_df_record_batch())
304 .map_err(Into::into)
305 }),
306 ))
307 }
308}