1use std::collections::BTreeMap;
16#[cfg(test)]
17use std::collections::HashMap;
18use std::env;
19use std::fmt::Display;
20use std::net::SocketAddr;
21use std::sync::Arc;
22use std::time::Duration;
23
24use api::v1::Basic;
25use auth::{
26 DefaultPermissionChecker, PermissionCheckerRef, UserProviderRef, user_provider_from_option,
27};
28use axum::Router;
29use catalog::kvbackend::KvBackendCatalogManager;
30use client::{Client, Database};
31use common_base::Plugins;
32use common_catalog::consts::MIN_USER_TABLE_ID;
33use common_config::Configurable;
34use common_event_recorder::EventRecorderOptions;
35#[cfg(test)]
36use common_meta::DatanodeId;
37use common_meta::key::TableMetadataManager;
38use common_meta::key::catalog_name::CatalogNameKey;
39use common_meta::key::schema_name::SchemaNameKey;
40use common_meta::kv_backend::KvBackendRef;
41use common_query::Output;
42use common_runtime::runtime::BuilderBuild;
43use common_runtime::{Builder as RuntimeBuilder, Runtime};
44use common_test_util::ports;
45use common_test_util::temp_dir::{TempDir, create_temp_dir};
46use common_wal::config::DatanodeWalConfig;
47use datanode::config::{DatanodeOptions, StorageConfig};
48#[cfg(test)]
49use datanode::datanode::Datanode;
50use frontend::instance::Instance;
51use frontend::service_config::{MysqlOptions, PostgresOptions};
52#[cfg(test)]
53use meta_srv::metasrv::Metasrv;
54use mito2::engine::MitoEngine;
55use mito2::gc::GcConfig;
56use object_store::config::{
57 AzblobConfig, FileConfig, GcsConfig, ObjectStoreConfig, OssConfig, S3Config,
58};
59use object_store::services::{Azblob, Gcs, Oss, S3};
60use object_store::test_util::TempFolder;
61use object_store::{AzblobConnection, GcsConnection, ObjectStore, OssConnection, S3Connection};
62use servers::batcher::logical_table::LogicalTablePendingRowsBatcher;
63use servers::grpc::builder::GrpcServerBuilder;
64use servers::grpc::greptime_handler::GreptimeRequestHandler;
65use servers::grpc::{FlightCompression, GrpcOptions, GrpcServer, GrpcServerConfig};
66use servers::http::{HttpOptions, HttpServerBuilder};
67use servers::metrics_handler::MetricsHandler;
68use servers::mysql::server::{MysqlServer, MysqlSpawnConfig, MysqlSpawnRef};
69use servers::otel_arrow::OtelArrowServiceHandler;
70use servers::postgres::PostgresServer;
71use servers::prom_remote_write::validation::PromValidationMode;
72use servers::query_handler::sql::SqlQueryHandler;
73use servers::request_memory_limiter::ServerMemoryLimiter;
74use servers::server::Server;
75use servers::tls::ReloadableTlsServerConfig;
76use session::context::QueryContext;
77use store_api::metric_engine_consts::METRIC_METADATA_REGION_GROUP;
78use store_api::region_engine::RegionEngine;
79use store_api::region_request::{RegionFlushRequest, RegionRequest};
80use store_api::storage::RegionId;
81
82use crate::cluster::{GreptimeDbCluster, GreptimeDbClusterBuilder};
83use crate::standalone::{GreptimeDbStandalone, GreptimeDbStandaloneBuilder};
84
85pub type WalSnapshot = BTreeMap<(u64, RegionId), (u64, u64)>;
87
88async fn flush_and_snapshot_region_wal(engine: &MitoEngine) -> WalSnapshot {
91 let mut snapshot = BTreeMap::new();
92 for region in engine.regions() {
93 let id = region.region_id();
94 if id.table_id() < MIN_USER_TABLE_ID || id.region_group() == METRIC_METADATA_REGION_GROUP {
95 continue;
96 }
97 engine
98 .handle_request(id, RegionRequest::Flush(RegionFlushRequest::default()))
99 .await
100 .unwrap();
101 let statistic = engine.region_statistic(id).unwrap();
102 snapshot.insert(
103 (0, id),
104 (
105 region.flushed_sequence(),
106 statistic.manifest.data_flushed_entry_id(),
107 ),
108 );
109 }
110 snapshot
111}
112
113pub fn assert_wal_delta(before: &WalSnapshot, after: &WalSnapshot, skip_wal: bool) {
115 assert_eq!(
116 before.keys().collect::<Vec<_>>(),
117 after.keys().collect::<Vec<_>>(),
118 "warm up table creation before taking the snapshot"
119 );
120 let mut written = 0;
121 for (id, &(flushed_sequence, flushed_entry_id)) in after {
122 let (previous_flushed_sequence, previous_flushed_entry_id) = before[id];
123 if flushed_sequence > previous_flushed_sequence {
124 written += 1;
125 if skip_wal {
126 assert_eq!(
127 flushed_entry_id, previous_flushed_entry_id,
128 "region {id:?} wrote WAL despite the request policy"
129 );
130 } else {
131 assert!(
132 flushed_entry_id > previous_flushed_entry_id,
133 "region {id:?} did not write WAL"
134 );
135 }
136 }
137 }
138 assert!(
139 written > 0,
140 "request must reach a data region, not merely return success"
141 );
142}
143
144pub enum MockInstanceImpl {
146 Standalone(GreptimeDbStandalone),
147 Distributed(GreptimeDbCluster),
148}
149
150impl MockInstanceImpl {
151 #[cfg(test)]
156 pub(crate) fn metasrv(&self) -> &Arc<Metasrv> {
157 match self {
158 Self::Standalone(_) => unreachable!(),
159 Self::Distributed(instance) => &instance.metasrv,
160 }
161 }
162
163 #[cfg(test)]
168 pub(crate) fn datanodes(&self) -> &HashMap<DatanodeId, Datanode> {
169 match self {
170 Self::Standalone(_) => unreachable!(),
171 Self::Distributed(instance) => &instance.datanode_instances,
172 }
173 }
174
175 pub async fn new(name: &str, distributed: bool) -> Self {
177 let name = format!(
178 "{name}_{}",
179 if distributed {
180 "distributed"
181 } else {
182 "standalone"
183 }
184 );
185 if distributed {
186 Self::Distributed(
189 GreptimeDbClusterBuilder::new(&name)
190 .await
191 .with_datanodes(3)
192 .build(false)
193 .await,
194 )
195 } else {
196 Self::Standalone(GreptimeDbStandaloneBuilder::new(&name).build().await)
197 }
198 }
199
200 pub fn frontend(&self) -> Arc<Instance> {
202 match self {
203 Self::Standalone(instance) => instance.fe_instance().clone(),
204 Self::Distributed(cluster) => cluster.fe_instance().clone(),
205 }
206 }
207
208 pub async fn flush_and_snapshot_wal(&self) -> WalSnapshot {
210 match self {
211 Self::Standalone(instance) => {
212 flush_and_snapshot_region_wal(&instance.mito_engine).await
213 }
214 Self::Distributed(cluster) => {
215 let mut result = BTreeMap::new();
216 for (node_id, datanode) in &cluster.datanode_instances {
217 let engine = datanode.region_server().mito_engine().unwrap();
218 result.extend(
219 flush_and_snapshot_region_wal(&engine)
220 .await
221 .into_iter()
222 .map(|((_, region_id), watermarks)| {
223 ((*node_id, region_id), watermarks)
224 }),
225 );
226 }
227 result
228 }
229 }
230 }
231
232 pub async fn shutdown(&mut self) {
234 match self {
235 Self::Standalone(instance) => instance.guard.remove_all().await,
236 Self::Distributed(cluster) => {
237 cluster.metasrv.shutdown().await.unwrap();
238 for datanode in cluster.datanode_instances.values_mut() {
239 datanode.shutdown().await.unwrap();
240 }
241 for guard in &mut cluster.guards {
242 guard.remove_all().await;
243 }
244 }
245 }
246 }
247}
248
249pub const PEER_PLACEHOLDER_ADDR: &str = "127.0.0.1:3001";
250
251#[derive(Debug, Clone, Copy, Eq, PartialEq)]
252pub enum StorageType {
253 S3,
254 S3WithCache,
255 File,
256 Oss,
257 Azblob,
258 Gcs,
259}
260
261impl Display for StorageType {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 match self {
264 StorageType::S3 => write!(f, "S3"),
265 StorageType::S3WithCache => write!(f, "S3"),
266 StorageType::File => write!(f, "File"),
267 StorageType::Oss => write!(f, "Oss"),
268 StorageType::Azblob => write!(f, "Azblob"),
269 StorageType::Gcs => write!(f, "Gcs"),
270 }
271 }
272}
273
274impl StorageType {
275 pub fn build_storage_types_based_on_env() -> Vec<StorageType> {
276 let mut storage_types = Vec::with_capacity(4);
277 storage_types.push(StorageType::File);
278 if let Ok(bucket) = env::var("GT_S3_BUCKET")
279 && !bucket.is_empty()
280 {
281 storage_types.push(StorageType::S3);
282 }
283 if env::var("GT_OSS_BUCKET").is_ok() {
284 storage_types.push(StorageType::Oss);
285 }
286 if env::var("GT_AZBLOB_CONTAINER").is_ok() {
287 storage_types.push(StorageType::Azblob);
288 }
289 if env::var("GT_GCS_BUCKET").is_ok() {
290 storage_types.push(StorageType::Gcs);
291 }
292 storage_types
293 }
294
295 pub fn test_on(&self) -> bool {
296 let _ = dotenv::dotenv();
297
298 match self {
299 StorageType::File => true, StorageType::S3 | StorageType::S3WithCache => {
301 if let Ok(b) = env::var("GT_S3_BUCKET") {
302 !b.is_empty()
303 } else {
304 false
305 }
306 }
307 StorageType::Oss => {
308 if let Ok(b) = env::var("GT_OSS_BUCKET") {
309 !b.is_empty()
310 } else {
311 false
312 }
313 }
314 StorageType::Azblob => {
315 if let Ok(b) = env::var("GT_AZBLOB_CONTAINER") {
316 !b.is_empty()
317 } else {
318 false
319 }
320 }
321 StorageType::Gcs => {
322 if let Ok(b) = env::var("GT_GCS_BUCKET") {
323 !b.is_empty()
324 } else {
325 false
326 }
327 }
328 }
329 }
330}
331
332pub fn test_event_recorder_options() -> EventRecorderOptions {
338 EventRecorderOptions {
339 flush_interval: Duration::from_millis(100),
340 ..Default::default()
341 }
342}
343
344fn s3_test_config() -> S3Config {
345 S3Config {
346 connection: S3Connection {
347 root: uuid::Uuid::new_v4().to_string(),
348 access_key_id: env::var("GT_S3_ACCESS_KEY_ID").unwrap().into(),
349 secret_access_key: env::var("GT_S3_ACCESS_KEY").unwrap().into(),
350 bucket: env::var("GT_S3_BUCKET").unwrap(),
351 region: Some(env::var("GT_S3_REGION").unwrap()),
352 endpoint: env::var("GT_S3_ENDPOINT_URL").ok(),
353 ..Default::default()
354 },
355 ..Default::default()
356 }
357}
358
359pub fn get_test_store_config(store_type: &StorageType) -> (ObjectStoreConfig, TempDirGuard) {
360 let _ = dotenv::dotenv();
361
362 match store_type {
363 StorageType::Gcs => {
364 let gcs_config = GcsConfig {
365 connection: GcsConnection {
366 root: uuid::Uuid::new_v4().to_string(),
367 bucket: env::var("GT_GCS_BUCKET").unwrap(),
368 scope: env::var("GT_GCS_SCOPE").unwrap(),
369 credential_path: env::var("GT_GCS_CREDENTIAL_PATH").unwrap().into(),
370 credential: env::var("GT_GCS_CREDENTIAL").unwrap().into(),
371 endpoint: env::var("GT_GCS_ENDPOINT").unwrap_or_default(),
372 },
373 ..Default::default()
374 };
375
376 let builder = Gcs::from(&gcs_config.connection);
377 let config = ObjectStoreConfig::Gcs(gcs_config);
378 let store = ObjectStore::new(builder).unwrap();
379 (config, TempDirGuard::remote(TempFolder::new(&store, "/")))
380 }
381 StorageType::Azblob => {
382 let azblob_config = AzblobConfig {
383 connection: AzblobConnection {
384 root: uuid::Uuid::new_v4().to_string(),
385 container: env::var("GT_AZBLOB_CONTAINER").unwrap(),
386 account_name: env::var("GT_AZBLOB_ACCOUNT_NAME").unwrap().into(),
387 account_key: env::var("GT_AZBLOB_ACCOUNT_KEY").unwrap().into(),
388 endpoint: env::var("GT_AZBLOB_ENDPOINT").unwrap(),
389 ..Default::default()
390 },
391 ..Default::default()
392 };
393
394 let builder = Azblob::from(&azblob_config.connection);
395 let config = ObjectStoreConfig::Azblob(azblob_config);
396 let store = ObjectStore::new(builder).unwrap();
397 (config, TempDirGuard::remote(TempFolder::new(&store, "/")))
398 }
399 StorageType::Oss => {
400 let oss_config = OssConfig {
401 connection: OssConnection {
402 root: uuid::Uuid::new_v4().to_string(),
403 access_key_id: env::var("GT_OSS_ACCESS_KEY_ID").unwrap().into(),
404 access_key_secret: env::var("GT_OSS_ACCESS_KEY").unwrap().into(),
405 bucket: env::var("GT_OSS_BUCKET").unwrap(),
406 endpoint: env::var("GT_OSS_ENDPOINT").unwrap(),
407 },
408 ..Default::default()
409 };
410
411 let builder = Oss::from(&oss_config.connection);
412 let config = ObjectStoreConfig::Oss(oss_config);
413 let store = ObjectStore::new(builder).unwrap();
414 (config, TempDirGuard::remote(TempFolder::new(&store, "/")))
415 }
416 StorageType::S3 | StorageType::S3WithCache => {
417 let mut s3_config = s3_test_config();
418
419 let cache_dir = if *store_type == StorageType::S3WithCache {
423 let dir = create_temp_dir("gt_s3_read_cache");
424 s3_config.cache.cache_path = dir.path().to_string_lossy().to_string();
425 Some(dir)
426 } else {
427 s3_config.cache.enable_read_cache = false;
428 None
429 };
430
431 let builder = S3::from(&s3_config.connection);
432 let config = ObjectStoreConfig::S3(s3_config);
433 let store = ObjectStore::new(builder).unwrap();
434 (
435 config,
436 TempDirGuard {
437 remote: Some(TempFolder::new(&store, "/")),
438 local_cache: cache_dir,
439 },
440 )
441 }
442 StorageType::File => (
443 ObjectStoreConfig::File(FileConfig {}),
444 TempDirGuard::default(),
445 ),
446 }
447}
448
449#[derive(Default)]
450pub struct TempDirGuard {
451 remote: Option<TempFolder>,
453 #[allow(dead_code)]
455 local_cache: Option<TempDir>,
456}
457
458impl TempDirGuard {
459 fn remote(folder: TempFolder) -> Self {
460 Self {
461 remote: Some(folder),
462 local_cache: None,
463 }
464 }
465}
466
467pub struct TestGuard {
468 pub home_guard: FileDirGuard,
469 pub storage_guards: Vec<StorageGuard>,
470}
471
472pub struct FileDirGuard {
473 pub temp_dir: TempDir,
474}
475
476impl FileDirGuard {
477 pub fn new(temp_dir: TempDir) -> Self {
478 Self { temp_dir }
479 }
480}
481
482pub struct StorageGuard(pub TempDirGuard);
483
484impl TestGuard {
485 pub async fn remove_all(&mut self) {
486 for storage_guard in self.storage_guards.iter_mut() {
487 if let Some(guard) = &mut storage_guard.0.remote {
488 guard.remove_all().await.unwrap()
489 }
490 }
491 }
492}
493
494impl Drop for TestGuard {
495 fn drop(&mut self) {
496 let (tx, rx) = std::sync::mpsc::channel();
497
498 let guards = std::mem::take(&mut self.storage_guards);
499 common_runtime::spawn_global(async move {
500 let mut errors = vec![];
501 for guard in guards {
502 if let Some(remote) = guard.0.remote
503 && let Err(e) = remote.remove_all().await
504 {
505 errors.push(e);
506 }
507 }
508 if errors.is_empty() {
509 tx.send(Ok(())).unwrap();
510 } else {
511 tx.send(Err(errors)).unwrap();
512 }
513 });
514 rx.recv().unwrap().unwrap_or_else(|e| panic!("{:?}", e));
515 }
516}
517
518pub fn create_tmp_dir_and_datanode_opts(
519 default_store_type: StorageType,
520 store_provider_types: Vec<StorageType>,
521 name: &str,
522 wal_config: DatanodeWalConfig,
523 gc_config: GcConfig,
524) -> (DatanodeOptions, TestGuard) {
525 let home_tmp_dir = create_temp_dir(&format!("gt_data_{name}"));
526 let home_dir = home_tmp_dir.path().to_str().unwrap().to_string();
527
528 let mut store_providers = Vec::with_capacity(store_provider_types.len());
530 let mut storage_guards = Vec::with_capacity(store_provider_types.len() + 1);
532
533 let (default_store, data_tmp_dir) = get_test_store_config(&default_store_type);
534 storage_guards.push(StorageGuard(data_tmp_dir));
535
536 for store_type in store_provider_types {
537 let (store, data_tmp_dir) = get_test_store_config(&store_type);
538 store_providers.push(store);
539 storage_guards.push(StorageGuard(data_tmp_dir))
540 }
541 let opts = create_datanode_opts(
542 default_store,
543 store_providers,
544 home_dir,
545 wal_config,
546 gc_config,
547 );
548
549 (
550 opts,
551 TestGuard {
552 home_guard: FileDirGuard::new(home_tmp_dir),
553 storage_guards,
554 },
555 )
556}
557
558pub(crate) fn create_datanode_opts(
559 default_store: ObjectStoreConfig,
560 providers: Vec<ObjectStoreConfig>,
561 home_dir: String,
562 wal_config: DatanodeWalConfig,
563 gc_config: GcConfig,
564) -> DatanodeOptions {
565 let region_engine = DatanodeOptions::default()
566 .region_engine
567 .into_iter()
568 .map(|mut v| {
569 if let datanode::config::RegionEngineConfig::Mito(mito_config) = &mut v {
570 mito_config.gc = gc_config.clone();
571 }
572 v
573 })
574 .collect();
575 DatanodeOptions {
576 node_id: Some(0),
577 require_lease_before_startup: true,
578 storage: StorageConfig {
579 data_home: home_dir,
580 copy_root: None,
581 providers,
582 store: default_store,
583 },
584 grpc: GrpcOptions::default()
585 .with_bind_addr(PEER_PLACEHOLDER_ADDR)
586 .with_server_addr(PEER_PLACEHOLDER_ADDR),
587 wal: wal_config,
588 region_engine,
589 ..Default::default()
590 }
591}
592
593pub(crate) async fn create_test_table(instance: &Instance, table_name: &str) {
594 let sql = format!(
595 r#"
596CREATE TABLE IF NOT EXISTS {table_name} (
597 host String NOT NULL PRIMARY KEY,
598 cpu DOUBLE NULL,
599 memory DOUBLE NULL,
600 ts TIMESTAMP NOT NULL TIME INDEX,
601)
602"#
603 );
604
605 let result = instance.do_query(&sql, QueryContext::arc()).await;
606 let _ = result.first().unwrap().as_ref().unwrap();
607}
608
609async fn setup_standalone_instance(
610 test_name: &str,
611 store_type: StorageType,
612) -> GreptimeDbStandalone {
613 GreptimeDbStandaloneBuilder::new(test_name)
614 .with_default_store_type(store_type)
615 .build()
616 .await
617}
618
619async fn setup_standalone_instance_with_slow_query_threshold(
620 test_name: &str,
621 store_type: StorageType,
622 slow_query_threshold: std::time::Duration,
623) -> GreptimeDbStandalone {
624 GreptimeDbStandaloneBuilder::new(test_name)
625 .with_default_store_type(store_type)
626 .with_slow_query_threshold(slow_query_threshold)
627 .build()
628 .await
629}
630
631async fn setup_standalone_instance_with_plugins(
632 test_name: &str,
633 store_type: StorageType,
634 plugins: Plugins,
635) -> GreptimeDbStandalone {
636 GreptimeDbStandaloneBuilder::new(test_name)
637 .with_default_store_type(store_type)
638 .with_plugin(plugins)
639 .build()
640 .await
641}
642
643async fn setup_standalone_instance_with_plugins_and_slow_query_threshold(
644 test_name: &str,
645 store_type: StorageType,
646 plugins: Plugins,
647 slow_query_threshold: std::time::Duration,
648) -> GreptimeDbStandalone {
649 GreptimeDbStandaloneBuilder::new(test_name)
650 .with_default_store_type(store_type)
651 .with_plugin(plugins)
652 .with_slow_query_threshold(slow_query_threshold)
653 .build()
654 .await
655}
656
657pub async fn setup_test_http_app(store_type: StorageType, name: &str) -> (Router, TestGuard) {
658 let instance = setup_standalone_instance(name, store_type).await;
659
660 let http_opts = HttpOptions {
661 addr: format!("127.0.0.1:{}", ports::get_port()),
662 ..Default::default()
663 };
664 let http_server = HttpServerBuilder::new(http_opts)
665 .with_sql_handler(instance.fe_instance().clone())
666 .with_logs_handler(instance.fe_instance().clone())
667 .with_metrics_handler(MetricsHandler)
668 .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
669 .build();
670 (
671 http_server.build(http_server.make_app()).unwrap(),
672 instance.guard,
673 )
674}
675
676pub async fn setup_test_http_app_with_frontend(
677 store_type: StorageType,
678 name: &str,
679) -> (Router, TestGuard) {
680 setup_test_http_app_with_frontend_and_user_provider(store_type, name, None).await
681}
682
683pub async fn setup_test_http_app_with_frontend_and_slow_query_threshold(
684 store_type: StorageType,
685 name: &str,
686 slow_query_threshold: std::time::Duration,
687) -> (Router, TestGuard) {
688 let instance =
689 setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
690 .await;
691
692 create_test_table(instance.fe_instance(), "demo").await;
693
694 let http_opts = HttpOptions {
695 addr: format!("127.0.0.1:{}", ports::get_port()),
696 ..Default::default()
697 };
698
699 let http_server = HttpServerBuilder::new(http_opts)
700 .with_sql_handler(instance.fe_instance().clone())
701 .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
702 .with_logs_handler(instance.fe_instance().clone())
703 .with_influxdb_handler(instance.fe_instance().clone())
704 .with_otlp_handler(instance.fe_instance().clone(), true)
705 .with_jaeger_handler(instance.fe_instance().clone())
706 .with_greptime_config_options(instance.opts.to_toml().unwrap())
707 .build();
708
709 let app = http_server.build(http_server.make_app()).unwrap();
710 (app, instance.guard)
711}
712
713pub async fn setup_test_http_app_with_frontend_and_user_provider(
714 store_type: StorageType,
715 name: &str,
716 user_provider: Option<UserProviderRef>,
717) -> (Router, TestGuard) {
718 setup_test_http_app_with_frontend_and_custom_options(
719 store_type,
720 name,
721 user_provider,
722 None,
723 None,
724 )
725 .await
726}
727
728pub async fn setup_test_http_app_with_frontend_and_custom_options(
729 store_type: StorageType,
730 name: &str,
731 user_provider: Option<UserProviderRef>,
732 http_opts: Option<HttpOptions>,
733 memory_limiter: Option<ServerMemoryLimiter>,
734) -> (Router, TestGuard) {
735 let plugins = Plugins::new();
736 if let Some(user_provider) = user_provider.clone() {
737 plugins.insert::<UserProviderRef>(user_provider.clone());
738 plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
739 }
740
741 let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
742
743 create_test_table(instance.fe_instance(), "demo").await;
744
745 let http_opts = http_opts.unwrap_or_else(|| HttpOptions {
746 addr: format!("127.0.0.1:{}", ports::get_port()),
747 ..Default::default()
748 });
749
750 let mut http_server = HttpServerBuilder::new(http_opts);
753 if let Some(limiter) = memory_limiter {
754 http_server = http_server.with_memory_limiter(limiter);
755 }
756 let mut http_server = http_server
757 .with_sql_handler(instance.fe_instance().clone())
758 .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
759 .with_logs_handler(instance.fe_instance().clone())
760 .with_influxdb_handler(instance.fe_instance().clone())
761 .with_otlp_handler(instance.fe_instance().clone(), true)
762 .with_prometheus_handler(instance.fe_instance().clone())
763 .with_jaeger_handler(instance.fe_instance().clone())
764 .with_dashboard_handler(instance.fe_instance().clone())
765 .with_greptime_config_options(instance.opts.to_toml().unwrap());
766
767 if let Some(user_provider) = user_provider {
768 http_server = http_server.with_user_provider(user_provider);
769 }
770
771 let http_server = http_server.build();
772
773 let app = http_server.build(http_server.make_app()).unwrap();
774 (app, instance.guard)
775}
776
777async fn run_sql(sql: &str, instance: &GreptimeDbStandalone) {
778 let result = instance
779 .fe_instance()
780 .do_query(sql, QueryContext::arc())
781 .await;
782 let _ = result.first().unwrap().as_ref().unwrap();
783}
784
785pub async fn setup_test_prom_app_with_frontend(
786 store_type: StorageType,
787 name: &str,
788) -> (Router, TestGuard) {
789 setup_test_prom_app_with_frontend_inner(store_type, name, false, None).await
790}
791
792pub async fn setup_test_prom_app_with_frontend_batched(
796 store_type: StorageType,
797 name: &str,
798) -> (Router, TestGuard) {
799 setup_test_prom_app_with_frontend_inner(store_type, name, true, None).await
800}
801
802pub async fn setup_test_prom_app_with_frontend_and_memory_limiter(
806 store_type: StorageType,
807 name: &str,
808 memory_limiter: Option<ServerMemoryLimiter>,
809) -> (Router, TestGuard) {
810 setup_test_prom_app_with_frontend_inner(store_type, name, false, memory_limiter).await
811}
812
813async fn setup_test_prom_app_with_frontend_inner(
814 store_type: StorageType,
815 name: &str,
816 enable_batcher: bool,
817 memory_limiter: Option<ServerMemoryLimiter>,
818) -> (Router, TestGuard) {
819 unsafe {
820 std::env::set_var("TZ", "UTC");
821 }
822
823 let instance = setup_standalone_instance(name, store_type).await;
824
825 let sql = "CREATE TABLE phy (ts timestamp time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
827 run_sql(sql, &instance).await;
828 let sql = "CREATE TABLE phy_ns (ts timestamp(0) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
829 run_sql(sql, &instance).await;
830 let sql = "CREATE TABLE demo (ts timestamp time index, val double, host string primary key) engine=metric with ('on_physical_table' = 'phy')";
832 run_sql(sql, &instance).await;
833 let sql = "CREATE TABLE demo_metrics (ts timestamp time index, val double, idc string primary key) engine=metric with ('on_physical_table' = 'phy')";
834 run_sql(sql, &instance).await;
835 let sql = "CREATE TABLE multi_labels (ts timestamp(0) time index, val double, idc string, env string, host string, primary key (idc, env, host)) engine=metric with ('on_physical_table' = 'phy_ns')";
836 run_sql(sql, &instance).await;
837
838 let sql = "INSERT INTO demo(host, val, ts) VALUES ('host1', 1.1, 0), ('host2', 2.1, 600000)";
840 run_sql(sql, &instance).await;
841 let sql =
842 "INSERT INTO demo_metrics(idc, val, ts) VALUES ('idc1', 1.1, 0), ('idc2', 2.1, 600000)";
843 run_sql(sql, &instance).await;
844 let sql = "INSERT INTO demo_metrics(val, ts) VALUES (1.1, 0)";
846 run_sql(sql, &instance).await;
847
848 let sql = "INSERT INTO multi_labels(idc, env, host, val, ts) VALUES ('idc1', 'dev', 'host1', 1.1, 0), ('idc1', 'dev', 'host2', 2.1, 0), ('idc2', 'dev', 'host1', 1.1, 0), ('idc2', 'test', 'host3', 2.1, 0)";
850 run_sql(sql, &instance).await;
851
852 let sql = "CREATE TABLE phy2 (ts timestamp(9) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
854 run_sql(sql, &instance).await;
855 let sql = "CREATE TABLE demo_metrics_with_nanos(ts timestamp(9) time index, val double, idc string primary key) engine=metric with ('on_physical_table' = 'phy2')";
856 run_sql(sql, &instance).await;
857 let sql = "INSERT INTO demo_metrics_with_nanos(idc, val, ts) VALUES ('idc1', 1.1, 0)";
858 run_sql(sql, &instance).await;
859
860 let sql = "CREATE TABLE mito (ts timestamp(9) time index, val double, host bigint primary key) engine=mito";
862 run_sql(sql, &instance).await;
863 let sql = "INSERT INTO mito(host, val, ts) VALUES (1, 1.1, 0)";
864 run_sql(sql, &instance).await;
865
866 let http_server = build_test_prom_server(
867 instance.fe_instance().clone(),
868 enable_batcher,
869 memory_limiter,
870 )
871 .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
872 .build();
873 let app = http_server.build(http_server.make_app()).unwrap();
874 (app, instance.guard)
875}
876
877pub fn build_test_prom_server(
881 frontend_ref: Arc<Instance>,
882 enable_batcher: bool,
883 memory_limiter: Option<ServerMemoryLimiter>,
884) -> HttpServerBuilder {
885 let http_opts = HttpOptions {
886 addr: format!("127.0.0.1:{}", ports::get_port()),
887 ..Default::default()
888 };
889 let pending_rows_batcher = if enable_batcher {
892 LogicalTablePendingRowsBatcher::try_new(
893 frontend_ref.partition_manager().clone(),
894 frontend_ref.node_manager().clone(),
895 frontend_ref.catalog_manager().clone(),
896 frontend_ref.table_flownode_set_cache().clone(),
897 true,
898 frontend_ref.clone(),
899 Duration::from_millis(50),
900 1000,
901 4,
902 64,
903 64,
904 std::num::NonZeroUsize::new(1024).unwrap(),
905 )
906 } else {
907 None
908 };
909 assert_eq!(pending_rows_batcher.is_some(), enable_batcher);
910 let mut builder = HttpServerBuilder::new(http_opts)
911 .with_sql_handler(frontend_ref.clone())
912 .with_logs_handler(frontend_ref.clone());
913 if let Some(limiter) = memory_limiter {
914 builder = builder.with_memory_limiter(limiter);
915 }
916 builder
917 .with_prom_handler(
918 frontend_ref.clone(),
919 Some(frontend_ref.clone()),
920 true,
921 PromValidationMode::Strict,
922 pending_rows_batcher,
923 )
924 .with_prometheus_handler(frontend_ref)
925}
926
927pub async fn setup_grpc_server(
928 store_type: StorageType,
929 name: &str,
930) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
931 setup_grpc_server_with(store_type, name, None, None, None).await
932}
933
934pub async fn setup_grpc_server_with_user_provider(
935 store_type: StorageType,
936 name: &str,
937 user_provider: Option<UserProviderRef>,
938) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
939 setup_grpc_server_with(store_type, name, user_provider, None, None).await
940}
941
942pub async fn setup_grpc_server_with_auto_create_table_disabled(
945 store_type: StorageType,
946 name: &str,
947) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
948 let instance = GreptimeDbStandaloneBuilder::new(name)
949 .with_default_store_type(store_type)
950 .with_auto_create_table(false)
951 .build()
952 .await;
953 setup_grpc_server_for_instance(instance, None, None, None).await
954}
955
956pub async fn setup_grpc_server_with(
957 store_type: StorageType,
958 name: &str,
959 user_provider: Option<UserProviderRef>,
960 grpc_config: Option<GrpcServerConfig>,
961 memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
962) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
963 let instance = setup_standalone_instance(name, store_type).await;
964 setup_grpc_server_for_instance(instance, user_provider, grpc_config, memory_limiter).await
965}
966
967async fn setup_grpc_server_for_instance(
970 instance: GreptimeDbStandalone,
971 user_provider: Option<UserProviderRef>,
972 grpc_config: Option<GrpcServerConfig>,
973 memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
974) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
975 let grpc_server = setup_grpc_server_for_frontend_instance_with(
976 instance.fe_instance().clone(),
977 user_provider,
978 grpc_config,
979 memory_limiter,
980 )
981 .await;
982 (instance, grpc_server)
983}
984
985pub async fn setup_grpc_server_for_frontend_instance(
987 instance: Arc<Instance>,
988 user_provider: Option<UserProviderRef>,
989) -> Arc<GrpcServer> {
990 setup_grpc_server_for_frontend_instance_with(instance, user_provider, None, None).await
991}
992
993pub async fn setup_authenticated_grpc_database(
996 instance: Arc<Instance>,
997 username: &str,
998 password: &str,
999) -> (Database, Arc<GrpcServer>) {
1000 let user_provider =
1001 user_provider_from_option(&format!("static_user_provider:cmd:{username}={password}"))
1002 .unwrap();
1003 let grpc_server = setup_grpc_server_for_frontend_instance(instance, Some(user_provider)).await;
1004 let grpc_addr = grpc_server.bind_addr().unwrap().to_string();
1005 let mut database = Database::new_with_dbname(
1006 "greptime-public",
1007 Client::with_query_and_control_managers(
1008 Default::default(),
1009 Default::default(),
1010 vec![grpc_addr],
1011 ),
1012 );
1013 database.set_auth(api::v1::auth_header::AuthScheme::Basic(Basic {
1014 username: username.to_string(),
1015 password: password.to_string(),
1016 }));
1017 (database, grpc_server)
1018}
1019
1020async fn setup_grpc_server_for_frontend_instance_with(
1021 fe_instance_ref: Arc<Instance>,
1022 user_provider: Option<UserProviderRef>,
1023 grpc_config: Option<GrpcServerConfig>,
1024 memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
1025) -> Arc<GrpcServer> {
1026 let runtime: Runtime = RuntimeBuilder::default()
1027 .worker_threads(2)
1028 .thread_name("grpc-handlers")
1029 .build()
1030 .unwrap();
1031
1032 let greptime_request_handler = GreptimeRequestHandler::new(
1033 fe_instance_ref.clone(),
1034 user_provider.clone(),
1035 Some(runtime.clone()),
1036 FlightCompression::default(),
1037 );
1038
1039 let flight_handler = Arc::new(greptime_request_handler.clone());
1040
1041 let grpc_config = grpc_config.unwrap_or_default();
1042 let mut grpc_builder = GrpcServerBuilder::new(grpc_config.clone(), runtime);
1043
1044 if let Some(limiter) = memory_limiter {
1045 grpc_builder = grpc_builder.with_memory_limiter(limiter);
1046 }
1047
1048 let grpc_builder = grpc_builder
1049 .database_handler(greptime_request_handler)
1050 .flight_handler(flight_handler)
1051 .prometheus_handler(fe_instance_ref.clone(), user_provider.clone())
1052 .otel_arrow_handler(OtelArrowServiceHandler::new(fe_instance_ref, user_provider))
1053 .with_tls_config(grpc_config.tls)
1054 .unwrap();
1055
1056 let mut grpc_server = grpc_builder.build();
1057
1058 let fe_grpc_addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
1059 grpc_server.start(fe_grpc_addr).await.unwrap();
1060
1061 Arc::new(grpc_server)
1062}
1063
1064pub async fn setup_mysql_server(
1065 store_type: StorageType,
1066 name: &str,
1067) -> (TestGuard, Arc<Box<dyn Server>>) {
1068 setup_mysql_server_with_user_provider(store_type, name, None).await
1069}
1070
1071pub async fn setup_mysql_server_with_slow_query_threshold(
1072 store_type: StorageType,
1073 name: &str,
1074 slow_query_threshold: std::time::Duration,
1075) -> (TestGuard, Arc<Box<dyn Server>>) {
1076 let plugins = Plugins::new();
1077 let instance = setup_standalone_instance_with_plugins_and_slow_query_threshold(
1078 name,
1079 store_type,
1080 plugins,
1081 slow_query_threshold,
1082 )
1083 .await;
1084
1085 let runtime = RuntimeBuilder::default()
1086 .worker_threads(2)
1087 .thread_name("mysql-runtime")
1088 .build()
1089 .unwrap();
1090
1091 let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1092
1093 let fe_instance_ref = instance.fe_instance().clone();
1094 let opts = MysqlOptions {
1095 addr: fe_mysql_addr.clone(),
1096 ..Default::default()
1097 };
1098 let mut mysql_server = MysqlServer::create_server(
1099 runtime,
1100 Arc::new(MysqlSpawnRef::new(fe_instance_ref, None)),
1101 Arc::new(MysqlSpawnConfig::new(
1102 false,
1103 Arc::new(
1104 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1105 .expect("Failed to load certificates and keys"),
1106 ),
1107 0,
1108 opts.reject_no_database.unwrap_or(false),
1109 opts.prepared_stmt_cache_size,
1110 )),
1111 None,
1112 );
1113
1114 mysql_server
1115 .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1116 .await
1117 .unwrap();
1118
1119 (instance.guard, Arc::new(mysql_server))
1120}
1121
1122pub async fn setup_mysql_server_with_user_provider(
1123 store_type: StorageType,
1124 name: &str,
1125 user_provider: Option<UserProviderRef>,
1126) -> (TestGuard, Arc<Box<dyn Server>>) {
1127 let plugins = Plugins::new();
1128 if let Some(user_provider) = user_provider.clone() {
1129 plugins.insert::<UserProviderRef>(user_provider.clone());
1130 plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
1131 }
1132
1133 let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
1134
1135 let runtime = RuntimeBuilder::default()
1136 .worker_threads(2)
1137 .thread_name("mysql-runtime")
1138 .build()
1139 .unwrap();
1140
1141 let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1142
1143 let fe_instance_ref = instance.fe_instance().clone();
1144 let opts = MysqlOptions {
1145 addr: fe_mysql_addr.clone(),
1146 ..Default::default()
1147 };
1148 let mut mysql_server = MysqlServer::create_server(
1149 runtime,
1150 Arc::new(MysqlSpawnRef::new(fe_instance_ref, user_provider)),
1151 Arc::new(MysqlSpawnConfig::new(
1152 false,
1153 Arc::new(
1154 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1155 .expect("Failed to load certificates and keys"),
1156 ),
1157 0,
1158 opts.reject_no_database.unwrap_or(false),
1159 opts.prepared_stmt_cache_size,
1160 )),
1161 None,
1162 );
1163
1164 mysql_server
1165 .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1166 .await
1167 .unwrap();
1168
1169 (instance.guard, Arc::new(mysql_server))
1170}
1171
1172pub async fn setup_pg_server(
1173 store_type: StorageType,
1174 name: &str,
1175) -> (TestGuard, Arc<Box<dyn Server>>) {
1176 setup_pg_server_with_user_provider(store_type, name, None).await
1177}
1178
1179pub async fn setup_pg_server_with_slow_query_threshold(
1180 store_type: StorageType,
1181 name: &str,
1182 slow_query_threshold: std::time::Duration,
1183) -> (TestGuard, Arc<Box<dyn Server>>) {
1184 let instance =
1185 setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
1186 .await;
1187
1188 let runtime = RuntimeBuilder::default()
1189 .worker_threads(2)
1190 .thread_name("pg-runtime")
1191 .build()
1192 .unwrap();
1193
1194 let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1195
1196 let fe_instance_ref = instance.fe_instance().clone();
1197 let opts = PostgresOptions {
1198 addr: fe_pg_addr.clone(),
1199 ..Default::default()
1200 };
1201 let tls_server_config = Arc::new(
1202 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1203 .expect("Failed to load certificates and keys"),
1204 );
1205
1206 let mut pg_server = Box::new(PostgresServer::new(
1207 fe_instance_ref,
1208 opts.tls.should_force_tls(),
1209 tls_server_config,
1210 0,
1211 runtime,
1212 None,
1213 None,
1214 ));
1215
1216 pg_server
1217 .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1218 .await
1219 .unwrap();
1220
1221 (instance.guard, Arc::new(pg_server))
1222}
1223
1224pub async fn setup_pg_server_with_user_provider(
1225 store_type: StorageType,
1226 name: &str,
1227 user_provider: Option<UserProviderRef>,
1228) -> (TestGuard, Arc<Box<dyn Server>>) {
1229 let instance = setup_standalone_instance(name, store_type).await;
1230
1231 let runtime = RuntimeBuilder::default()
1232 .worker_threads(2)
1233 .thread_name("pg-runtime")
1234 .build()
1235 .unwrap();
1236
1237 let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1238
1239 let fe_instance_ref = instance.fe_instance().clone();
1240 let opts = PostgresOptions {
1241 addr: fe_pg_addr.clone(),
1242 ..Default::default()
1243 };
1244 let tls_server_config = Arc::new(
1245 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1246 .expect("Failed to load certificates and keys"),
1247 );
1248
1249 let mut pg_server = Box::new(PostgresServer::new(
1250 fe_instance_ref,
1251 opts.tls.should_force_tls(),
1252 tls_server_config,
1253 0,
1254 runtime,
1255 user_provider,
1256 None,
1257 ));
1258
1259 pg_server
1260 .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1261 .await
1262 .unwrap();
1263
1264 (instance.guard, Arc::new(pg_server))
1265}
1266
1267pub async fn setup_pg_server_with_prom_native_histogram(
1272 store_type: StorageType,
1273 name: &str,
1274) -> (TestGuard, Router, Arc<Box<dyn Server>>) {
1275 unsafe {
1276 std::env::set_var("TZ", "UTC");
1277 }
1278
1279 let instance = setup_standalone_instance(name, store_type).await;
1280
1281 let http_server = build_test_prom_server(instance.fe_instance().clone(), false, None)
1283 .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
1284 .build();
1285 let app = http_server.build(http_server.make_app()).unwrap();
1286
1287 let runtime = RuntimeBuilder::default()
1289 .worker_threads(2)
1290 .thread_name("pg-runtime")
1291 .build()
1292 .unwrap();
1293
1294 let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1295 let opts = PostgresOptions {
1296 addr: fe_pg_addr.clone(),
1297 ..Default::default()
1298 };
1299 let tls_server_config = Arc::new(
1300 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1301 .expect("Failed to load certificates and keys"),
1302 );
1303
1304 let mut pg_server = Box::new(PostgresServer::new(
1305 instance.fe_instance().clone(),
1306 opts.tls.should_force_tls(),
1307 tls_server_config,
1308 0,
1309 runtime,
1310 None,
1311 None,
1312 ));
1313
1314 pg_server
1315 .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1316 .await
1317 .unwrap();
1318
1319 (instance.guard, app, Arc::new(pg_server))
1320}
1321
1322pub(crate) async fn prepare_another_catalog_and_schema(instance: &Instance) {
1323 let catalog_manager = instance
1324 .catalog_manager()
1325 .as_any()
1326 .downcast_ref::<KvBackendCatalogManager>()
1327 .unwrap();
1328
1329 let table_metadata_manager = catalog_manager.table_metadata_manager_ref();
1330 prepare_another_catalog_and_schema_with_manager(table_metadata_manager).await;
1331}
1332
1333pub(crate) async fn prepare_another_catalog_and_schema_with_kv_backend(kv_backend: KvBackendRef) {
1334 let table_metadata_manager = TableMetadataManager::new(kv_backend);
1335 prepare_another_catalog_and_schema_with_manager(&table_metadata_manager).await;
1336}
1337
1338async fn prepare_another_catalog_and_schema_with_manager(
1339 table_metadata_manager: &TableMetadataManager,
1340) {
1341 table_metadata_manager
1342 .catalog_manager()
1343 .create(CatalogNameKey::new("another_catalog"), true)
1344 .await
1345 .unwrap();
1346 table_metadata_manager
1347 .schema_manager()
1348 .create(
1349 SchemaNameKey::new("another_catalog", "another_schema"),
1350 None,
1351 true,
1352 )
1353 .await
1354 .unwrap();
1355}
1356
1357pub async fn execute_sql(instance: &Arc<Instance>, sql: &str) -> Output {
1358 SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1359 .await
1360 .remove(0)
1361 .unwrap()
1362}
1363
1364pub async fn try_execute_sql(
1365 instance: &Arc<Instance>,
1366 sql: &str,
1367) -> servers::error::Result<Output> {
1368 SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1369 .await
1370 .remove(0)
1371}
1372
1373pub async fn execute_sql_and_expect(instance: &Arc<Instance>, sql: &str, expected: &str) {
1374 let output = execute_sql(instance, sql).await;
1375 let output = output.data.pretty_print().await;
1376 assert_eq!(output, expected.trim());
1377}