1use std::collections::{BTreeMap, HashMap};
16use std::future::Future;
17use std::sync::Arc;
18use std::time::Duration;
19
20use async_trait::async_trait;
21use clap::Parser;
22use common_error::ext::BoxedError;
23use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
24use common_meta::kv_backend::etcd::EtcdStore;
25use common_meta::kv_backend::memory::MemoryKvBackend;
26#[cfg(feature = "mysql_kvbackend")]
27use common_meta::kv_backend::rds::MySqlStore;
28#[cfg(feature = "pg_kvbackend")]
29use common_meta::kv_backend::rds::PgStore;
30use common_meta::peer::Peer;
31use common_meta::rpc::router::{Region, RegionRoute};
32use common_telemetry::info;
33use common_wal::options::WalOptions;
34use datatypes::data_type::ConcreteDataType;
35use datatypes::schema::{ColumnSchema, RawSchema};
36use rand::Rng;
37use store_api::storage::RegionNumber;
38use table::metadata::{RawTableInfo, RawTableMeta, TableId, TableIdent, TableType};
39use table::table_name::TableName;
40
41use self::metadata::TableMetadataBencher;
42use crate::Tool;
43
44mod metadata;
45
46async fn bench_self_recorded<F, Fut>(desc: &str, f: F, count: u32)
47where
48 F: Fn(u32) -> Fut,
49 Fut: Future<Output = Duration>,
50{
51 let mut total = Duration::default();
52
53 for i in 1..=count {
54 total += f(i).await;
55 }
56
57 let cost = total.as_millis() as f64 / count as f64;
58 info!("{desc}, average operation cost: {cost:.2} ms");
59}
60
61#[derive(Debug, Default, Parser)]
63pub struct BenchTableMetadataCommand {
64 #[clap(long)]
65 etcd_addr: Option<String>,
66 #[cfg(feature = "pg_kvbackend")]
67 #[clap(long)]
68 postgres_addr: Option<String>,
69 #[cfg(feature = "mysql_kvbackend")]
70 #[clap(long)]
71 mysql_addr: Option<String>,
72 #[clap(long)]
73 count: u32,
74}
75
76impl BenchTableMetadataCommand {
77 pub async fn build(&self) -> std::result::Result<Box<dyn Tool>, BoxedError> {
78 let kv_backend = if let Some(etcd_addr) = &self.etcd_addr {
79 info!("Using etcd as kv backend");
80 EtcdStore::with_endpoints([etcd_addr], 128).await.unwrap()
81 } else {
82 Arc::new(MemoryKvBackend::new())
83 };
84
85 #[cfg(feature = "pg_kvbackend")]
86 let kv_backend = if let Some(postgres_addr) = &self.postgres_addr {
87 info!("Using postgres as kv backend");
88 PgStore::with_url(postgres_addr, "greptime_metakv", 128)
89 .await
90 .unwrap()
91 } else {
92 kv_backend
93 };
94
95 #[cfg(feature = "mysql_kvbackend")]
96 let kv_backend = if let Some(mysql_addr) = &self.mysql_addr {
97 info!("Using mysql as kv backend");
98 MySqlStore::with_url(mysql_addr, "greptime_metakv", 128)
99 .await
100 .unwrap()
101 } else {
102 kv_backend
103 };
104
105 let table_metadata_manager = Arc::new(TableMetadataManager::new(kv_backend));
106
107 let tool = BenchTableMetadata {
108 table_metadata_manager,
109 count: self.count,
110 };
111 Ok(Box::new(tool))
112 }
113}
114
115struct BenchTableMetadata {
116 table_metadata_manager: TableMetadataManagerRef,
117 count: u32,
118}
119
120#[async_trait]
121impl Tool for BenchTableMetadata {
122 async fn do_work(&self) -> std::result::Result<(), BoxedError> {
123 let bencher = TableMetadataBencher::new(self.table_metadata_manager.clone(), self.count);
124 bencher.bench_create().await;
125 bencher.bench_get().await;
126 bencher.bench_rename().await;
127 bencher.bench_delete().await;
128 Ok(())
129 }
130}
131
132fn create_table_info(table_id: TableId, table_name: TableName) -> RawTableInfo {
133 let columns = 100;
134 let mut column_schemas = Vec::with_capacity(columns);
135 column_schemas.push(
136 ColumnSchema::new(
137 "ts",
138 ConcreteDataType::timestamp_millisecond_datatype(),
139 true,
140 )
141 .with_time_index(true),
142 );
143
144 for i in 1..columns {
145 let column_name = format!("my_column_{i}");
146 column_schemas.push(ColumnSchema::new(
147 column_name,
148 ConcreteDataType::string_datatype(),
149 true,
150 ));
151 }
152
153 let meta = RawTableMeta {
154 schema: RawSchema::new(column_schemas),
155 engine: "mito".to_string(),
156 created_on: chrono::DateTime::default(),
157 primary_key_indices: vec![],
158 next_column_id: columns as u32 + 1,
159 value_indices: vec![],
160 options: Default::default(),
161 region_numbers: (1..=100).collect(),
162 partition_key_indices: vec![],
163 };
164
165 RawTableInfo {
166 ident: TableIdent {
167 table_id,
168 version: 1,
169 },
170 name: table_name.table_name,
171 desc: Some("blah".to_string()),
172 catalog_name: table_name.catalog_name,
173 schema_name: table_name.schema_name,
174 meta,
175 table_type: TableType::Base,
176 }
177}
178
179fn create_region_routes(regions: Vec<RegionNumber>) -> Vec<RegionRoute> {
180 let mut region_routes = Vec::with_capacity(100);
181 let mut rng = rand::rng();
182
183 for region_id in regions.into_iter().map(u64::from) {
184 region_routes.push(RegionRoute {
185 region: Region {
186 id: region_id.into(),
187 name: String::new(),
188 partition: None,
189 attrs: BTreeMap::new(),
190 },
191 leader_peer: Some(Peer {
192 id: rng.random_range(0..10),
193 addr: String::new(),
194 }),
195 follower_peers: vec![],
196 leader_state: None,
197 leader_down_since: None,
198 });
199 }
200
201 region_routes
202}
203
204fn create_region_wal_options(regions: Vec<RegionNumber>) -> HashMap<RegionNumber, WalOptions> {
205 let _ = regions;
207 HashMap::default()
208}