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 common_time::timestamp::TimeUnit;
24use datafusion::execution::TaskContext;
25use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
26use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
27use datatypes::prelude::{ConcreteDataType, ScalarVectorBuilder, VectorRef};
28use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
29use datatypes::timestamp::TimestampMillisecond;
30use datatypes::value::Value;
31use datatypes::vectors::{
32 StringVectorBuilder, TimestampMillisecondVectorBuilder, UInt32VectorBuilder,
33 UInt64VectorBuilder,
34};
35use snafu::ResultExt;
36use store_api::storage::{ScanRequest, TableId};
37
38use crate::CatalogManager;
39use crate::error::{CreateRecordBatchSnafu, InternalSnafu, Result};
40use crate::information_schema::Predicates;
41use crate::system_schema::SystemTable;
42use crate::system_schema::information_schema::{
43 InformationSchemaTableScope, InformationTable, REGION_STATISTICS,
44};
45use crate::system_schema::utils;
46
47const REGION_ID: &str = "region_id";
48const TABLE_ID: &str = "table_id";
49const REGION_NUMBER: &str = "region_number";
50const REGION_ROWS: &str = "region_rows";
51const WRITTEN_BYTES: &str = "written_bytes_since_open";
52const QUERY_CPU_TIME_MILLIS: &str = "query_cpu_time_millis";
53const QUERY_SCANNED_BYTES: &str = "query_scanned_bytes";
54const DISK_SIZE: &str = "disk_size";
55const MEMTABLE_SIZE: &str = "memtable_size";
56const MANIFEST_SIZE: &str = "manifest_size";
57const SST_SIZE: &str = "sst_size";
58const SST_NUM: &str = "sst_num";
59const INDEX_SIZE: &str = "index_size";
60const ENGINE: &str = "engine";
61const REGION_ROLE: &str = "region_role";
62const MIN_TIMESTAMP: &str = "min_timestamp";
63const MAX_TIMESTAMP: &str = "max_timestamp";
64
65const INIT_CAPACITY: usize = 42;
66
67#[derive(Debug)]
89pub(super) struct InformationSchemaRegionStatistics {
90 schema: SchemaRef,
91 catalog_manager: Weak<dyn CatalogManager>,
92}
93
94impl InformationSchemaRegionStatistics {
95 pub(super) fn new(catalog_manager: Weak<dyn CatalogManager>) -> Self {
96 Self {
97 schema: Self::schema(),
98 catalog_manager,
99 }
100 }
101
102 pub(crate) fn schema() -> SchemaRef {
103 Arc::new(Schema::new(vec![
104 ColumnSchema::new(REGION_ID, ConcreteDataType::uint64_datatype(), false),
105 ColumnSchema::new(TABLE_ID, ConcreteDataType::uint32_datatype(), false),
106 ColumnSchema::new(REGION_NUMBER, ConcreteDataType::uint32_datatype(), false),
107 ColumnSchema::new(REGION_ROWS, ConcreteDataType::uint64_datatype(), true),
108 ColumnSchema::new(WRITTEN_BYTES, ConcreteDataType::uint64_datatype(), true),
109 ColumnSchema::new(
110 QUERY_CPU_TIME_MILLIS,
111 ConcreteDataType::uint64_datatype(),
112 true,
113 ),
114 ColumnSchema::new(
115 QUERY_SCANNED_BYTES,
116 ConcreteDataType::uint64_datatype(),
117 true,
118 ),
119 ColumnSchema::new(DISK_SIZE, ConcreteDataType::uint64_datatype(), true),
120 ColumnSchema::new(MEMTABLE_SIZE, ConcreteDataType::uint64_datatype(), true),
121 ColumnSchema::new(MANIFEST_SIZE, ConcreteDataType::uint64_datatype(), true),
122 ColumnSchema::new(SST_SIZE, ConcreteDataType::uint64_datatype(), true),
123 ColumnSchema::new(SST_NUM, ConcreteDataType::uint64_datatype(), true),
124 ColumnSchema::new(INDEX_SIZE, ConcreteDataType::uint64_datatype(), true),
125 ColumnSchema::new(ENGINE, ConcreteDataType::string_datatype(), true),
126 ColumnSchema::new(REGION_ROLE, ConcreteDataType::string_datatype(), true),
127 ColumnSchema::new(
128 MIN_TIMESTAMP,
129 ConcreteDataType::timestamp_millisecond_datatype(),
130 true,
131 ),
132 ColumnSchema::new(
133 MAX_TIMESTAMP,
134 ConcreteDataType::timestamp_millisecond_datatype(),
135 true,
136 ),
137 ]))
138 }
139
140 fn builder(&self) -> InformationSchemaRegionStatisticsBuilder {
141 InformationSchemaRegionStatisticsBuilder::new(
142 self.schema.clone(),
143 self.catalog_manager.clone(),
144 )
145 }
146}
147
148impl InformationTable for InformationSchemaRegionStatistics {
149 fn scope(&self) -> InformationSchemaTableScope {
150 InformationSchemaTableScope::Global
151 }
152}
153
154impl SystemTable for InformationSchemaRegionStatistics {
155 fn table_id(&self) -> TableId {
156 INFORMATION_SCHEMA_REGION_STATISTICS_TABLE_ID
157 }
158
159 fn table_name(&self) -> &'static str {
160 REGION_STATISTICS
161 }
162
163 fn schema(&self) -> SchemaRef {
164 self.schema.clone()
165 }
166
167 fn to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream> {
168 let schema = self.schema.arrow_schema().clone();
169 let mut builder = self.builder();
170
171 let stream = Box::pin(DfRecordBatchStreamAdapter::new(
172 schema,
173 futures::stream::once(async move {
174 builder
175 .make_region_statistics(Some(request))
176 .await
177 .map(|x| x.into_df_record_batch())
178 .map_err(Into::into)
179 }),
180 ));
181
182 Ok(Box::pin(
183 RecordBatchStreamAdapter::try_new(stream)
184 .map_err(BoxedError::new)
185 .context(InternalSnafu)?,
186 ))
187 }
188}
189
190struct InformationSchemaRegionStatisticsBuilder {
191 schema: SchemaRef,
192 catalog_manager: Weak<dyn CatalogManager>,
193
194 region_ids: UInt64VectorBuilder,
195 table_ids: UInt32VectorBuilder,
196 region_numbers: UInt32VectorBuilder,
197 region_rows: UInt64VectorBuilder,
198 written_bytes: UInt64VectorBuilder,
199 query_cpu_time_millis: UInt64VectorBuilder,
200 query_scanned_bytes: UInt64VectorBuilder,
201 disk_sizes: UInt64VectorBuilder,
202 memtable_sizes: UInt64VectorBuilder,
203 manifest_sizes: UInt64VectorBuilder,
204 sst_sizes: UInt64VectorBuilder,
205 sst_nums: UInt64VectorBuilder,
206 index_sizes: UInt64VectorBuilder,
207 engines: StringVectorBuilder,
208 region_roles: StringVectorBuilder,
209 min_timestamps: TimestampMillisecondVectorBuilder,
210 max_timestamps: TimestampMillisecondVectorBuilder,
211}
212
213impl InformationSchemaRegionStatisticsBuilder {
214 fn new(schema: SchemaRef, catalog_manager: Weak<dyn CatalogManager>) -> Self {
215 Self {
216 schema,
217 catalog_manager,
218 region_ids: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
219 table_ids: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
220 region_numbers: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
221 region_rows: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
222 written_bytes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
223 query_cpu_time_millis: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
224 query_scanned_bytes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
225 disk_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
226 memtable_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
227 manifest_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
228 sst_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
229 sst_nums: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
230 index_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
231 engines: StringVectorBuilder::with_capacity(INIT_CAPACITY),
232 region_roles: StringVectorBuilder::with_capacity(INIT_CAPACITY),
233 min_timestamps: TimestampMillisecondVectorBuilder::with_capacity(INIT_CAPACITY),
234 max_timestamps: TimestampMillisecondVectorBuilder::with_capacity(INIT_CAPACITY),
235 }
236 }
237
238 async fn make_region_statistics(
240 &mut self,
241 request: Option<ScanRequest>,
242 ) -> Result<RecordBatch> {
243 let predicates = Predicates::from_scan_request(&request);
244 let information_extension = utils::information_extension(&self.catalog_manager)?;
245 let region_stats = information_extension.region_stats().await?;
246 for region_stat in region_stats {
247 self.add_region_statistic(&predicates, region_stat);
248 }
249 self.finish()
250 }
251
252 fn add_region_statistic(&mut self, predicate: &Predicates, region_stat: RegionStat) {
253 let row = [
254 (REGION_ID, &Value::from(region_stat.id.as_u64())),
255 (TABLE_ID, &Value::from(region_stat.id.table_id())),
256 (REGION_NUMBER, &Value::from(region_stat.id.region_number())),
257 (REGION_ROWS, &Value::from(region_stat.num_rows)),
258 (WRITTEN_BYTES, &Value::from(region_stat.written_bytes)),
259 (
260 QUERY_CPU_TIME_MILLIS,
261 &Value::from(region_stat.query_cpu_time / 1_000_000),
262 ),
263 (
264 QUERY_SCANNED_BYTES,
265 &Value::from(region_stat.query_scanned_bytes),
266 ),
267 (DISK_SIZE, &Value::from(region_stat.approximate_bytes)),
268 (MEMTABLE_SIZE, &Value::from(region_stat.memtable_size)),
269 (MANIFEST_SIZE, &Value::from(region_stat.manifest_size)),
270 (SST_SIZE, &Value::from(region_stat.sst_size)),
271 (SST_NUM, &Value::from(region_stat.sst_num)),
272 (INDEX_SIZE, &Value::from(region_stat.index_size)),
273 (ENGINE, &Value::from(region_stat.engine.as_str())),
274 (REGION_ROLE, &Value::from(region_stat.role.to_string())),
275 ];
276
277 if !predicate.eval(&row) {
278 return;
279 }
280
281 self.region_ids.push(Some(region_stat.id.as_u64()));
282 self.table_ids.push(Some(region_stat.id.table_id()));
283 self.region_numbers
284 .push(Some(region_stat.id.region_number()));
285 self.region_rows.push(Some(region_stat.num_rows));
286 self.written_bytes.push(Some(region_stat.written_bytes));
287 self.query_cpu_time_millis
288 .push(Some(region_stat.query_cpu_time / 1_000_000));
289 self.query_scanned_bytes
290 .push(Some(region_stat.query_scanned_bytes));
291 self.disk_sizes.push(Some(region_stat.approximate_bytes));
292 self.memtable_sizes.push(Some(region_stat.memtable_size));
293 self.manifest_sizes.push(Some(region_stat.manifest_size));
294 self.sst_sizes.push(Some(region_stat.sst_size));
295 self.sst_nums.push(Some(region_stat.sst_num));
296 self.index_sizes.push(Some(region_stat.index_size));
297 self.engines.push(Some(®ion_stat.engine));
298 self.region_roles.push(Some(®ion_stat.role.to_string()));
299 self.min_timestamps.push(
302 region_stat
303 .min_timestamp
304 .and_then(|ts| ts.convert_to(TimeUnit::Millisecond))
305 .map(TimestampMillisecond),
306 );
307 self.max_timestamps.push(
308 region_stat
309 .max_timestamp
310 .and_then(|ts| ts.convert_to_ceil(TimeUnit::Millisecond))
311 .map(TimestampMillisecond),
312 );
313 }
314
315 fn finish(&mut self) -> Result<RecordBatch> {
316 let columns: Vec<VectorRef> = vec![
317 Arc::new(self.region_ids.finish()),
318 Arc::new(self.table_ids.finish()),
319 Arc::new(self.region_numbers.finish()),
320 Arc::new(self.region_rows.finish()),
321 Arc::new(self.written_bytes.finish()),
322 Arc::new(self.query_cpu_time_millis.finish()),
323 Arc::new(self.query_scanned_bytes.finish()),
324 Arc::new(self.disk_sizes.finish()),
325 Arc::new(self.memtable_sizes.finish()),
326 Arc::new(self.manifest_sizes.finish()),
327 Arc::new(self.sst_sizes.finish()),
328 Arc::new(self.sst_nums.finish()),
329 Arc::new(self.index_sizes.finish()),
330 Arc::new(self.engines.finish()),
331 Arc::new(self.region_roles.finish()),
332 Arc::new(self.min_timestamps.finish()),
333 Arc::new(self.max_timestamps.finish()),
334 ];
335
336 RecordBatch::new(self.schema.clone(), columns).context(CreateRecordBatchSnafu)
337 }
338}
339
340impl DfPartitionStream for InformationSchemaRegionStatistics {
341 fn schema(&self) -> &ArrowSchemaRef {
342 self.schema.arrow_schema()
343 }
344
345 fn execute(&self, _: Arc<TaskContext>) -> DfSendableRecordBatchStream {
346 let schema = self.schema.arrow_schema().clone();
347 let mut builder = self.builder();
348 Box::pin(DfRecordBatchStreamAdapter::new(
349 schema,
350 futures::stream::once(async move {
351 builder
352 .make_region_statistics(None)
353 .await
354 .map(|x| x.into_df_record_batch())
355 .map_err(Into::into)
356 }),
357 ))
358 }
359}