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 probe_on_startup: false,
582 providers,
583 store: default_store,
584 },
585 grpc: GrpcOptions::default()
586 .with_bind_addr(PEER_PLACEHOLDER_ADDR)
587 .with_server_addr(PEER_PLACEHOLDER_ADDR),
588 wal: wal_config,
589 region_engine,
590 ..Default::default()
591 }
592}
593
594pub(crate) async fn create_test_table(instance: &Instance, table_name: &str) {
595 let sql = format!(
596 r#"
597CREATE TABLE IF NOT EXISTS {table_name} (
598 host String NOT NULL PRIMARY KEY,
599 cpu DOUBLE NULL,
600 memory DOUBLE NULL,
601 ts TIMESTAMP NOT NULL TIME INDEX,
602)
603"#
604 );
605
606 let result = instance.do_query(&sql, QueryContext::arc()).await;
607 let _ = result.first().unwrap().as_ref().unwrap();
608}
609
610async fn setup_standalone_instance(
611 test_name: &str,
612 store_type: StorageType,
613) -> GreptimeDbStandalone {
614 GreptimeDbStandaloneBuilder::new(test_name)
615 .with_default_store_type(store_type)
616 .build()
617 .await
618}
619
620async fn setup_standalone_instance_with_slow_query_threshold(
621 test_name: &str,
622 store_type: StorageType,
623 slow_query_threshold: std::time::Duration,
624) -> GreptimeDbStandalone {
625 GreptimeDbStandaloneBuilder::new(test_name)
626 .with_default_store_type(store_type)
627 .with_slow_query_threshold(slow_query_threshold)
628 .build()
629 .await
630}
631
632async fn setup_standalone_instance_with_plugins(
633 test_name: &str,
634 store_type: StorageType,
635 plugins: Plugins,
636) -> GreptimeDbStandalone {
637 GreptimeDbStandaloneBuilder::new(test_name)
638 .with_default_store_type(store_type)
639 .with_plugin(plugins)
640 .build()
641 .await
642}
643
644async fn setup_standalone_instance_with_plugins_and_slow_query_threshold(
645 test_name: &str,
646 store_type: StorageType,
647 plugins: Plugins,
648 slow_query_threshold: std::time::Duration,
649) -> GreptimeDbStandalone {
650 GreptimeDbStandaloneBuilder::new(test_name)
651 .with_default_store_type(store_type)
652 .with_plugin(plugins)
653 .with_slow_query_threshold(slow_query_threshold)
654 .build()
655 .await
656}
657
658pub async fn setup_test_http_app(store_type: StorageType, name: &str) -> (Router, TestGuard) {
659 let instance = setup_standalone_instance(name, store_type).await;
660
661 let http_opts = HttpOptions {
662 addr: format!("127.0.0.1:{}", ports::get_port()),
663 ..Default::default()
664 };
665 let http_server = HttpServerBuilder::new(http_opts)
666 .with_sql_handler(instance.fe_instance().clone())
667 .with_logs_handler(instance.fe_instance().clone())
668 .with_metrics_handler(MetricsHandler)
669 .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
670 .build();
671 (
672 http_server.build(http_server.make_app()).unwrap(),
673 instance.guard,
674 )
675}
676
677pub async fn setup_test_http_app_with_frontend(
678 store_type: StorageType,
679 name: &str,
680) -> (Router, TestGuard) {
681 setup_test_http_app_with_frontend_and_user_provider(store_type, name, None).await
682}
683
684pub async fn setup_test_http_app_with_frontend_and_slow_query_threshold(
685 store_type: StorageType,
686 name: &str,
687 slow_query_threshold: std::time::Duration,
688) -> (Router, TestGuard) {
689 let instance =
690 setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
691 .await;
692
693 create_test_table(instance.fe_instance(), "demo").await;
694
695 let http_opts = HttpOptions {
696 addr: format!("127.0.0.1:{}", ports::get_port()),
697 ..Default::default()
698 };
699
700 let http_server = HttpServerBuilder::new(http_opts)
701 .with_sql_handler(instance.fe_instance().clone())
702 .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
703 .with_logs_handler(instance.fe_instance().clone())
704 .with_influxdb_handler(instance.fe_instance().clone())
705 .with_otlp_handler(instance.fe_instance().clone(), true)
706 .with_jaeger_handler(instance.fe_instance().clone())
707 .with_greptime_config_options(instance.opts.to_toml().unwrap())
708 .build();
709
710 let app = http_server.build(http_server.make_app()).unwrap();
711 (app, instance.guard)
712}
713
714pub async fn setup_test_http_app_with_frontend_and_user_provider(
715 store_type: StorageType,
716 name: &str,
717 user_provider: Option<UserProviderRef>,
718) -> (Router, TestGuard) {
719 setup_test_http_app_with_frontend_and_custom_options(
720 store_type,
721 name,
722 user_provider,
723 None,
724 None,
725 )
726 .await
727}
728
729pub async fn setup_test_http_app_with_frontend_and_custom_options(
730 store_type: StorageType,
731 name: &str,
732 user_provider: Option<UserProviderRef>,
733 http_opts: Option<HttpOptions>,
734 memory_limiter: Option<ServerMemoryLimiter>,
735) -> (Router, TestGuard) {
736 let plugins = Plugins::new();
737 if let Some(user_provider) = user_provider.clone() {
738 plugins.insert::<UserProviderRef>(user_provider.clone());
739 plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
740 }
741
742 let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
743
744 create_test_table(instance.fe_instance(), "demo").await;
745
746 let http_opts = http_opts.unwrap_or_else(|| HttpOptions {
747 addr: format!("127.0.0.1:{}", ports::get_port()),
748 ..Default::default()
749 });
750
751 let mut http_server = HttpServerBuilder::new(http_opts);
754 if let Some(limiter) = memory_limiter {
755 http_server = http_server.with_memory_limiter(limiter);
756 }
757 let mut http_server = http_server
758 .with_sql_handler(instance.fe_instance().clone())
759 .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
760 .with_logs_handler(instance.fe_instance().clone())
761 .with_influxdb_handler(instance.fe_instance().clone())
762 .with_otlp_handler(instance.fe_instance().clone(), true)
763 .with_prometheus_handler(instance.fe_instance().clone())
764 .with_jaeger_handler(instance.fe_instance().clone())
765 .with_dashboard_handler(instance.fe_instance().clone())
766 .with_greptime_config_options(instance.opts.to_toml().unwrap());
767
768 if let Some(user_provider) = user_provider {
769 http_server = http_server.with_user_provider(user_provider);
770 }
771
772 let http_server = http_server.build();
773
774 let app = http_server.build(http_server.make_app()).unwrap();
775 (app, instance.guard)
776}
777
778async fn run_sql(sql: &str, instance: &GreptimeDbStandalone) {
779 let result = instance
780 .fe_instance()
781 .do_query(sql, QueryContext::arc())
782 .await;
783 let _ = result.first().unwrap().as_ref().unwrap();
784}
785
786pub async fn setup_test_prom_app_with_frontend(
787 store_type: StorageType,
788 name: &str,
789) -> (Router, TestGuard) {
790 setup_test_prom_app_with_frontend_inner(store_type, name, false, None).await
791}
792
793pub async fn setup_test_prom_app_with_frontend_batched(
797 store_type: StorageType,
798 name: &str,
799) -> (Router, TestGuard) {
800 setup_test_prom_app_with_frontend_inner(store_type, name, true, None).await
801}
802
803pub async fn setup_test_prom_app_with_frontend_and_memory_limiter(
807 store_type: StorageType,
808 name: &str,
809 memory_limiter: Option<ServerMemoryLimiter>,
810) -> (Router, TestGuard) {
811 setup_test_prom_app_with_frontend_inner(store_type, name, false, memory_limiter).await
812}
813
814async fn setup_test_prom_app_with_frontend_inner(
815 store_type: StorageType,
816 name: &str,
817 enable_batcher: bool,
818 memory_limiter: Option<ServerMemoryLimiter>,
819) -> (Router, TestGuard) {
820 unsafe {
821 std::env::set_var("TZ", "UTC");
822 }
823
824 let instance = setup_standalone_instance(name, store_type).await;
825
826 let sql = "CREATE TABLE phy (ts timestamp time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
828 run_sql(sql, &instance).await;
829 let sql = "CREATE TABLE phy_ns (ts timestamp(0) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
830 run_sql(sql, &instance).await;
831 let sql = "CREATE TABLE demo (ts timestamp time index, val double, host string primary key) engine=metric with ('on_physical_table' = 'phy')";
833 run_sql(sql, &instance).await;
834 let sql = "CREATE TABLE demo_metrics (ts timestamp time index, val double, idc string primary key) engine=metric with ('on_physical_table' = 'phy')";
835 run_sql(sql, &instance).await;
836 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')";
837 run_sql(sql, &instance).await;
838
839 let sql = "INSERT INTO demo(host, val, ts) VALUES ('host1', 1.1, 0), ('host2', 2.1, 600000)";
841 run_sql(sql, &instance).await;
842 let sql =
843 "INSERT INTO demo_metrics(idc, val, ts) VALUES ('idc1', 1.1, 0), ('idc2', 2.1, 600000)";
844 run_sql(sql, &instance).await;
845 let sql = "INSERT INTO demo_metrics(val, ts) VALUES (1.1, 0)";
847 run_sql(sql, &instance).await;
848
849 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)";
851 run_sql(sql, &instance).await;
852
853 let sql = "CREATE TABLE phy2 (ts timestamp(9) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
855 run_sql(sql, &instance).await;
856 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')";
857 run_sql(sql, &instance).await;
858 let sql = "INSERT INTO demo_metrics_with_nanos(idc, val, ts) VALUES ('idc1', 1.1, 0)";
859 run_sql(sql, &instance).await;
860
861 let sql = "CREATE TABLE mito (ts timestamp(9) time index, val double, host bigint primary key) engine=mito";
863 run_sql(sql, &instance).await;
864 let sql = "INSERT INTO mito(host, val, ts) VALUES (1, 1.1, 0)";
865 run_sql(sql, &instance).await;
866
867 let http_server = build_test_prom_server(
868 instance.fe_instance().clone(),
869 enable_batcher,
870 memory_limiter,
871 )
872 .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
873 .build();
874 let app = http_server.build(http_server.make_app()).unwrap();
875 (app, instance.guard)
876}
877
878pub fn build_test_prom_server(
882 frontend_ref: Arc<Instance>,
883 enable_batcher: bool,
884 memory_limiter: Option<ServerMemoryLimiter>,
885) -> HttpServerBuilder {
886 let http_opts = HttpOptions {
887 addr: format!("127.0.0.1:{}", ports::get_port()),
888 ..Default::default()
889 };
890 let pending_rows_batcher = if enable_batcher {
893 LogicalTablePendingRowsBatcher::try_new(
894 frontend_ref.partition_manager().clone(),
895 frontend_ref.node_manager().clone(),
896 frontend_ref.inserter().write_dispatcher().clone(),
897 frontend_ref.catalog_manager().clone(),
898 frontend_ref.table_flownode_set_cache().clone(),
899 true,
900 frontend_ref.clone(),
901 Duration::from_millis(50),
902 1000,
903 4,
904 64,
905 64,
906 std::num::NonZeroUsize::new(1024).unwrap(),
907 )
908 } else {
909 None
910 };
911 assert_eq!(pending_rows_batcher.is_some(), enable_batcher);
912 let mut builder = HttpServerBuilder::new(http_opts)
913 .with_sql_handler(frontend_ref.clone())
914 .with_logs_handler(frontend_ref.clone());
915 if let Some(limiter) = memory_limiter {
916 builder = builder.with_memory_limiter(limiter);
917 }
918 builder
919 .with_prom_handler(
920 frontend_ref.clone(),
921 Some(frontend_ref.clone()),
922 true,
923 PromValidationMode::Strict,
924 pending_rows_batcher,
925 )
926 .with_prometheus_handler(frontend_ref)
927}
928
929pub async fn setup_grpc_server(
930 store_type: StorageType,
931 name: &str,
932) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
933 setup_grpc_server_with(store_type, name, None, None, None).await
934}
935
936pub async fn setup_grpc_server_with_user_provider(
937 store_type: StorageType,
938 name: &str,
939 user_provider: Option<UserProviderRef>,
940) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
941 setup_grpc_server_with(store_type, name, user_provider, None, None).await
942}
943
944pub async fn setup_grpc_server_with_auto_create_table_disabled(
947 store_type: StorageType,
948 name: &str,
949) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
950 let instance = GreptimeDbStandaloneBuilder::new(name)
951 .with_default_store_type(store_type)
952 .with_auto_create_table(false)
953 .build()
954 .await;
955 setup_grpc_server_for_instance(instance, None, None, None).await
956}
957
958pub async fn setup_grpc_server_with(
959 store_type: StorageType,
960 name: &str,
961 user_provider: Option<UserProviderRef>,
962 grpc_config: Option<GrpcServerConfig>,
963 memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
964) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
965 let instance = setup_standalone_instance(name, store_type).await;
966 setup_grpc_server_for_instance(instance, user_provider, grpc_config, memory_limiter).await
967}
968
969async fn setup_grpc_server_for_instance(
972 instance: GreptimeDbStandalone,
973 user_provider: Option<UserProviderRef>,
974 grpc_config: Option<GrpcServerConfig>,
975 memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
976) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
977 let grpc_server = setup_grpc_server_for_frontend_instance_with(
978 instance.fe_instance().clone(),
979 user_provider,
980 grpc_config,
981 memory_limiter,
982 )
983 .await;
984 (instance, grpc_server)
985}
986
987pub async fn setup_grpc_server_for_frontend_instance(
989 instance: Arc<Instance>,
990 user_provider: Option<UserProviderRef>,
991) -> Arc<GrpcServer> {
992 setup_grpc_server_for_frontend_instance_with(instance, user_provider, None, None).await
993}
994
995pub async fn setup_authenticated_grpc_database(
998 instance: Arc<Instance>,
999 username: &str,
1000 password: &str,
1001) -> (Database, Arc<GrpcServer>) {
1002 let user_provider =
1003 user_provider_from_option(&format!("static_user_provider:cmd:{username}={password}"))
1004 .unwrap();
1005 let grpc_server = setup_grpc_server_for_frontend_instance(instance, Some(user_provider)).await;
1006 let grpc_addr = grpc_server.bind_addr().unwrap().to_string();
1007 let mut database = Database::new_with_dbname(
1008 "greptime-public",
1009 Client::with_query_and_control_managers(
1010 Default::default(),
1011 Default::default(),
1012 vec![grpc_addr],
1013 ),
1014 );
1015 database.set_auth(api::v1::auth_header::AuthScheme::Basic(Basic {
1016 username: username.to_string(),
1017 password: password.to_string(),
1018 }));
1019 (database, grpc_server)
1020}
1021
1022async fn setup_grpc_server_for_frontend_instance_with(
1023 fe_instance_ref: Arc<Instance>,
1024 user_provider: Option<UserProviderRef>,
1025 grpc_config: Option<GrpcServerConfig>,
1026 memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
1027) -> Arc<GrpcServer> {
1028 let runtime: Runtime = RuntimeBuilder::default()
1029 .worker_threads(2)
1030 .thread_name("grpc-handlers")
1031 .build()
1032 .unwrap();
1033
1034 let greptime_request_handler = GreptimeRequestHandler::new(
1035 fe_instance_ref.clone(),
1036 user_provider.clone(),
1037 Some(runtime.clone()),
1038 FlightCompression::default(),
1039 );
1040
1041 let flight_handler = Arc::new(greptime_request_handler.clone());
1042
1043 let grpc_config = grpc_config.unwrap_or_default();
1044 let mut grpc_builder = GrpcServerBuilder::new(grpc_config.clone(), runtime);
1045
1046 if let Some(limiter) = memory_limiter {
1047 grpc_builder = grpc_builder.with_memory_limiter(limiter);
1048 }
1049
1050 let grpc_builder = grpc_builder
1051 .database_handler(greptime_request_handler)
1052 .flight_handler(flight_handler)
1053 .prometheus_handler(fe_instance_ref.clone(), user_provider.clone())
1054 .otel_arrow_handler(OtelArrowServiceHandler::new(fe_instance_ref, user_provider))
1055 .with_tls_config(grpc_config.tls)
1056 .unwrap();
1057
1058 let mut grpc_server = grpc_builder.build();
1059
1060 let fe_grpc_addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
1061 grpc_server.start(fe_grpc_addr).await.unwrap();
1062
1063 Arc::new(grpc_server)
1064}
1065
1066pub async fn setup_mysql_server(
1067 store_type: StorageType,
1068 name: &str,
1069) -> (TestGuard, Arc<Box<dyn Server>>) {
1070 setup_mysql_server_with_user_provider(store_type, name, None).await
1071}
1072
1073pub async fn setup_mysql_server_with_slow_query_threshold(
1074 store_type: StorageType,
1075 name: &str,
1076 slow_query_threshold: std::time::Duration,
1077) -> (TestGuard, Arc<Box<dyn Server>>) {
1078 let plugins = Plugins::new();
1079 let instance = setup_standalone_instance_with_plugins_and_slow_query_threshold(
1080 name,
1081 store_type,
1082 plugins,
1083 slow_query_threshold,
1084 )
1085 .await;
1086
1087 let runtime = RuntimeBuilder::default()
1088 .worker_threads(2)
1089 .thread_name("mysql-runtime")
1090 .build()
1091 .unwrap();
1092
1093 let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1094
1095 let fe_instance_ref = instance.fe_instance().clone();
1096 let opts = MysqlOptions {
1097 addr: fe_mysql_addr.clone(),
1098 ..Default::default()
1099 };
1100 let mut mysql_server = MysqlServer::create_server(
1101 runtime,
1102 Arc::new(MysqlSpawnRef::new(fe_instance_ref, None)),
1103 Arc::new(MysqlSpawnConfig::new(
1104 false,
1105 Arc::new(
1106 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1107 .expect("Failed to load certificates and keys"),
1108 ),
1109 0,
1110 opts.reject_no_database.unwrap_or(false),
1111 opts.prepared_stmt_cache_size,
1112 )),
1113 None,
1114 );
1115
1116 mysql_server
1117 .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1118 .await
1119 .unwrap();
1120
1121 (instance.guard, Arc::new(mysql_server))
1122}
1123
1124pub async fn setup_mysql_server_with_user_provider(
1125 store_type: StorageType,
1126 name: &str,
1127 user_provider: Option<UserProviderRef>,
1128) -> (TestGuard, Arc<Box<dyn Server>>) {
1129 let plugins = Plugins::new();
1130 if let Some(user_provider) = user_provider.clone() {
1131 plugins.insert::<UserProviderRef>(user_provider.clone());
1132 plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
1133 }
1134
1135 let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
1136
1137 let runtime = RuntimeBuilder::default()
1138 .worker_threads(2)
1139 .thread_name("mysql-runtime")
1140 .build()
1141 .unwrap();
1142
1143 let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1144
1145 let fe_instance_ref = instance.fe_instance().clone();
1146 let opts = MysqlOptions {
1147 addr: fe_mysql_addr.clone(),
1148 ..Default::default()
1149 };
1150 let mut mysql_server = MysqlServer::create_server(
1151 runtime,
1152 Arc::new(MysqlSpawnRef::new(fe_instance_ref, user_provider)),
1153 Arc::new(MysqlSpawnConfig::new(
1154 false,
1155 Arc::new(
1156 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1157 .expect("Failed to load certificates and keys"),
1158 ),
1159 0,
1160 opts.reject_no_database.unwrap_or(false),
1161 opts.prepared_stmt_cache_size,
1162 )),
1163 None,
1164 );
1165
1166 mysql_server
1167 .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1168 .await
1169 .unwrap();
1170
1171 (instance.guard, Arc::new(mysql_server))
1172}
1173
1174pub async fn setup_pg_server(
1175 store_type: StorageType,
1176 name: &str,
1177) -> (TestGuard, Arc<Box<dyn Server>>) {
1178 setup_pg_server_with_user_provider(store_type, name, None).await
1179}
1180
1181pub async fn setup_pg_server_with_slow_query_threshold(
1182 store_type: StorageType,
1183 name: &str,
1184 slow_query_threshold: std::time::Duration,
1185) -> (TestGuard, Arc<Box<dyn Server>>) {
1186 let instance =
1187 setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
1188 .await;
1189
1190 let runtime = RuntimeBuilder::default()
1191 .worker_threads(2)
1192 .thread_name("pg-runtime")
1193 .build()
1194 .unwrap();
1195
1196 let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1197
1198 let fe_instance_ref = instance.fe_instance().clone();
1199 let opts = PostgresOptions {
1200 addr: fe_pg_addr.clone(),
1201 ..Default::default()
1202 };
1203 let tls_server_config = Arc::new(
1204 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1205 .expect("Failed to load certificates and keys"),
1206 );
1207
1208 let mut pg_server = Box::new(
1209 PostgresServer::new(
1210 fe_instance_ref.clone(),
1211 fe_instance_ref.clone(),
1212 opts.tls.should_force_tls(),
1213 tls_server_config,
1214 0,
1215 runtime,
1216 None,
1217 None,
1218 )
1219 .with_influxdb_handler(fe_instance_ref.clone()),
1220 );
1221
1222 pg_server
1223 .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1224 .await
1225 .unwrap();
1226
1227 (instance.guard, Arc::new(pg_server))
1228}
1229
1230pub async fn setup_pg_server_with_user_provider(
1231 store_type: StorageType,
1232 name: &str,
1233 user_provider: Option<UserProviderRef>,
1234) -> (TestGuard, Arc<Box<dyn Server>>) {
1235 let instance = setup_standalone_instance(name, store_type).await;
1236
1237 let runtime = RuntimeBuilder::default()
1238 .worker_threads(2)
1239 .thread_name("pg-runtime")
1240 .build()
1241 .unwrap();
1242
1243 let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1244
1245 let fe_instance_ref = instance.fe_instance().clone();
1246 let opts = PostgresOptions {
1247 addr: fe_pg_addr.clone(),
1248 ..Default::default()
1249 };
1250 let tls_server_config = Arc::new(
1251 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1252 .expect("Failed to load certificates and keys"),
1253 );
1254
1255 let mut pg_server = Box::new(
1256 PostgresServer::new(
1257 fe_instance_ref.clone(),
1258 fe_instance_ref.clone(),
1259 opts.tls.should_force_tls(),
1260 tls_server_config,
1261 0,
1262 runtime,
1263 user_provider,
1264 None,
1265 )
1266 .with_influxdb_handler(fe_instance_ref.clone()),
1267 );
1268
1269 pg_server
1270 .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1271 .await
1272 .unwrap();
1273
1274 (instance.guard, Arc::new(pg_server))
1275}
1276
1277pub async fn setup_pg_server_with_prom_native_histogram(
1282 store_type: StorageType,
1283 name: &str,
1284) -> (TestGuard, Router, Arc<Box<dyn Server>>) {
1285 unsafe {
1286 std::env::set_var("TZ", "UTC");
1287 }
1288
1289 let instance = setup_standalone_instance(name, store_type).await;
1290
1291 let http_server = build_test_prom_server(instance.fe_instance().clone(), false, None)
1293 .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
1294 .build();
1295 let app = http_server.build(http_server.make_app()).unwrap();
1296
1297 let runtime = RuntimeBuilder::default()
1299 .worker_threads(2)
1300 .thread_name("pg-runtime")
1301 .build()
1302 .unwrap();
1303
1304 let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1305 let opts = PostgresOptions {
1306 addr: fe_pg_addr.clone(),
1307 ..Default::default()
1308 };
1309 let tls_server_config = Arc::new(
1310 ReloadableTlsServerConfig::try_new(opts.tls.clone())
1311 .expect("Failed to load certificates and keys"),
1312 );
1313
1314 let mut pg_server = Box::new(
1315 PostgresServer::new(
1316 instance.fe_instance().clone(),
1317 instance.fe_instance().clone(),
1318 opts.tls.should_force_tls(),
1319 tls_server_config,
1320 0,
1321 runtime,
1322 None,
1323 None,
1324 )
1325 .with_influxdb_handler(instance.fe_instance().clone()),
1326 );
1327
1328 pg_server
1329 .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1330 .await
1331 .unwrap();
1332
1333 (instance.guard, app, Arc::new(pg_server))
1334}
1335
1336pub(crate) async fn prepare_another_catalog_and_schema(instance: &Instance) {
1337 let catalog_manager = instance
1338 .catalog_manager()
1339 .as_any()
1340 .downcast_ref::<KvBackendCatalogManager>()
1341 .unwrap();
1342
1343 let table_metadata_manager = catalog_manager.table_metadata_manager_ref();
1344 prepare_another_catalog_and_schema_with_manager(table_metadata_manager).await;
1345}
1346
1347pub(crate) async fn prepare_another_catalog_and_schema_with_kv_backend(kv_backend: KvBackendRef) {
1348 let table_metadata_manager = TableMetadataManager::new(kv_backend);
1349 prepare_another_catalog_and_schema_with_manager(&table_metadata_manager).await;
1350}
1351
1352async fn prepare_another_catalog_and_schema_with_manager(
1353 table_metadata_manager: &TableMetadataManager,
1354) {
1355 table_metadata_manager
1356 .catalog_manager()
1357 .create(CatalogNameKey::new("another_catalog"), true)
1358 .await
1359 .unwrap();
1360 table_metadata_manager
1361 .schema_manager()
1362 .create(
1363 SchemaNameKey::new("another_catalog", "another_schema"),
1364 None,
1365 true,
1366 )
1367 .await
1368 .unwrap();
1369}
1370
1371pub async fn execute_sql(instance: &Arc<Instance>, sql: &str) -> Output {
1372 SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1373 .await
1374 .remove(0)
1375 .unwrap()
1376}
1377
1378pub async fn try_execute_sql(
1379 instance: &Arc<Instance>,
1380 sql: &str,
1381) -> servers::error::Result<Output> {
1382 SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1383 .await
1384 .remove(0)
1385}
1386
1387pub async fn execute_sql_and_expect(instance: &Arc<Instance>, sql: &str, expected: &str) {
1388 let output = execute_sql(instance, sql).await;
1389 let output = output.data.pretty_print().await;
1390 assert_eq!(output, expected.trim());
1391}