1use std::collections::HashMap;
16use std::collections::hash_map::Entry;
17use std::fmt::{Debug, Display, Formatter};
18use std::sync::atomic::{AtomicU32, Ordering};
19use std::sync::{Arc, RwLock};
20use std::time::{Duration, Instant, UNIX_EPOCH};
21
22use api::v1::frontend::{KillProcessRequest, ListProcessRequest, ProcessInfo};
23use common_base::cancellation::CancellationHandle;
24use common_event_recorder::EventRecorderRef;
25use common_frontend::selector::{FrontendSelector, MetaClientSelector};
26use common_frontend::slow_query_event::SlowQueryEvent;
27use common_telemetry::logging::SlowQueriesRecordType;
28use common_telemetry::{debug, info, slow};
29use common_time::util::current_time_millis;
30use meta_client::MetaClientRef;
31use promql_parser::parser::EvalStmt;
32use rand::random;
33use snafu::{OptionExt, ResultExt, ensure};
34use sql::statements::statement::Statement;
35
36use crate::error;
37use crate::metrics::{PROCESS_KILL_COUNT, PROCESS_LIST_COUNT};
38
39pub type ProcessId = u32;
40pub type ProcessManagerRef = Arc<ProcessManager>;
41
42pub struct ProcessManager {
44 peer_addr: String,
52 next_id: AtomicU32,
54 catalogs: RwLock<HashMap<String, HashMap<ProcessId, CancellableProcess>>>,
56 frontend_selector: Option<MetaClientSelector>,
58}
59
60#[derive(Debug, Clone)]
63pub enum QueryStatement {
64 Sql(Statement),
65 Promql(EvalStmt, Option<String>),
67 Plan(String),
69}
70
71impl Display for QueryStatement {
72 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73 match self {
74 QueryStatement::Sql(stmt) => write!(f, "{}", stmt),
75 QueryStatement::Promql(eval_stmt, alias) => {
76 if let Some(alias) = alias {
77 write!(f, "{} AS {}", eval_stmt, alias)
78 } else {
79 write!(f, "{}", eval_stmt)
80 }
81 }
82 QueryStatement::Plan(query) => write!(f, "{}", query),
83 }
84 }
85}
86
87impl ProcessManager {
88 pub fn new(peer_addr: String, meta_client: Option<MetaClientRef>) -> Self {
94 let frontend_selector = meta_client.map(MetaClientSelector::new);
95 Self {
96 peer_addr,
97 next_id: Default::default(),
98 catalogs: Default::default(),
99 frontend_selector,
100 }
101 }
102}
103
104impl ProcessManager {
105 #[must_use]
107 pub fn register_query(
108 self: &Arc<Self>,
109 catalog: String,
110 schemas: Vec<String>,
111 query: String,
112 client: String,
113 query_id: Option<ProcessId>,
114 _slow_query_timer: Option<SlowQueryTimer>,
115 ) -> Ticket {
116 let id = query_id.unwrap_or_else(|| self.next_id.fetch_add(1, Ordering::Relaxed));
117 let process = ProcessInfo {
118 id,
119 catalog: catalog.clone(),
120 schemas,
121 query,
122 start_timestamp: current_time_millis(),
123 client,
124 frontend: self.peer_addr.clone(),
125 };
126 let cancellation_handle = Arc::new(CancellationHandle::default());
127 let cancellable_process = CancellableProcess::new(cancellation_handle.clone(), process);
128
129 self.catalogs
130 .write()
131 .unwrap()
132 .entry(catalog.clone())
133 .or_default()
134 .insert(id, cancellable_process);
135
136 Ticket {
137 catalog,
138 manager: self.clone(),
139 id,
140 cancellation_handle,
141 _slow_query_timer,
142 }
143 }
144
145 pub fn next_id(&self) -> u32 {
147 self.next_id.fetch_add(1, Ordering::Relaxed)
148 }
149
150 pub fn deregister_query(&self, catalog: String, id: ProcessId) {
152 if let Entry::Occupied(mut o) = self.catalogs.write().unwrap().entry(catalog) {
153 let process = o.get_mut().remove(&id);
154 debug!("Deregister process: {:?}", process);
155 if o.get().is_empty() {
156 o.remove();
157 }
158 }
159 }
160
161 pub fn local_processes(&self, catalog: Option<&str>) -> error::Result<Vec<ProcessInfo>> {
163 let catalogs = self.catalogs.read().unwrap();
164 let result = if let Some(catalog) = catalog {
165 if let Some(catalogs) = catalogs.get(catalog) {
166 catalogs.values().map(|p| p.process.clone()).collect()
167 } else {
168 vec![]
169 }
170 } else {
171 catalogs
172 .values()
173 .flat_map(|v| v.values().map(|p| p.process.clone()))
174 .collect()
175 };
176 Ok(result)
177 }
178
179 pub async fn list_all_processes(
187 &self,
188 catalog: Option<&str>,
189 ) -> error::Result<Vec<ProcessInfo>> {
190 let mut processes = vec![];
191 if let Some(remote_frontend_selector) = self.frontend_selector.as_ref() {
192 let frontends = remote_frontend_selector
193 .select(|peer| peer.addr != self.peer_addr)
194 .await
195 .context(error::InvokeFrontendSnafu)?;
196 for mut f in frontends {
197 let resp = f
198 .list_process(ListProcessRequest {
199 catalog: catalog.unwrap_or_default().to_string(),
200 })
201 .await
202 .context(error::InvokeFrontendSnafu)?;
203 processes.extend(resp.processes);
204 }
205 }
206 processes.extend(self.local_processes(catalog)?);
207 Ok(processes)
208 }
209
210 pub async fn kill_process(
215 &self,
216 peer_addr: String,
217 catalog: String,
218 id: ProcessId,
219 ) -> error::Result<bool> {
220 if peer_addr == self.peer_addr {
221 self.kill_local_process(catalog, id).await
222 } else {
223 let mut nodes = self
224 .frontend_selector
225 .as_ref()
226 .context(error::MetaClientMissingSnafu)?
227 .select(|peer| peer.addr == peer_addr)
228 .await
229 .context(error::InvokeFrontendSnafu)?;
230 ensure!(
231 !nodes.is_empty(),
232 error::FrontendNotFoundSnafu { addr: peer_addr }
233 );
234
235 let request = KillProcessRequest {
236 server_addr: peer_addr,
237 catalog,
238 process_id: id,
239 };
240 nodes[0]
241 .kill_process(request)
242 .await
243 .context(error::InvokeFrontendSnafu)?;
244 Ok(true)
245 }
246 }
247
248 pub async fn kill_local_process(&self, catalog: String, id: ProcessId) -> error::Result<bool> {
250 if let Some(catalogs) = self.catalogs.write().unwrap().get_mut(&catalog) {
251 if let Some(process) = catalogs.remove(&id) {
252 process.handle.cancel();
253 info!(
254 "Killed process, catalog: {}, id: {:?}",
255 process.process.catalog, process.process.id
256 );
257 PROCESS_KILL_COUNT.with_label_values(&[&catalog]).inc();
258 Ok(true)
259 } else {
260 debug!("Failed to kill process, id not found: {}", id);
261 Ok(false)
262 }
263 } else {
264 debug!("Failed to kill process, catalog not found: {}", catalog);
265 Ok(false)
266 }
267 }
268}
269
270pub struct Ticket {
271 pub(crate) catalog: String,
272 pub(crate) manager: ProcessManagerRef,
273 pub(crate) id: ProcessId,
274 pub cancellation_handle: Arc<CancellationHandle>,
275
276 _slow_query_timer: Option<SlowQueryTimer>,
278}
279
280impl Drop for Ticket {
281 fn drop(&mut self) {
282 self.manager
283 .deregister_query(std::mem::take(&mut self.catalog), self.id);
284 }
285}
286
287struct CancellableProcess {
288 handle: Arc<CancellationHandle>,
289 process: ProcessInfo,
290}
291
292impl Drop for CancellableProcess {
293 fn drop(&mut self) {
294 PROCESS_LIST_COUNT
295 .with_label_values(&[&self.process.catalog])
296 .dec();
297 }
298}
299
300impl CancellableProcess {
301 fn new(handle: Arc<CancellationHandle>, process: ProcessInfo) -> Self {
302 PROCESS_LIST_COUNT
303 .with_label_values(&[&process.catalog])
304 .inc();
305 Self { handle, process }
306 }
307}
308
309impl Debug for CancellableProcess {
310 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
311 f.debug_struct("CancellableProcess")
312 .field("cancelled", &self.handle.is_cancelled())
313 .field("process", &self.process)
314 .finish()
315 }
316}
317
318pub struct SlowQueryTimer {
321 start: Instant,
322 stmt: QueryStatement,
323 schema_name: String,
324 threshold: Duration,
325 sample_ratio: f64,
326 record_type: SlowQueriesRecordType,
327 recorder: EventRecorderRef,
328 record_state: Arc<RwLock<SlowQueryRecordState>>,
329}
330
331#[derive(Default)]
332struct SlowQueryRecordState {
333 force_record: bool,
334 payload: serde_json::Value,
335}
336
337#[derive(Clone)]
339pub struct SlowQueryRecorder {
340 record_state: Arc<RwLock<SlowQueryRecordState>>,
341}
342
343impl SlowQueryRecorder {
344 pub fn force_record_with_payload(&self, payload: serde_json::Value) {
346 let mut state = self.record_state.write().unwrap();
347 state.payload = payload;
348 state.force_record = true;
349 }
350}
351
352impl SlowQueryTimer {
353 pub fn new(
354 stmt: QueryStatement,
355 schema_name: String,
356 threshold: Duration,
357 sample_ratio: f64,
358 record_type: SlowQueriesRecordType,
359 recorder: EventRecorderRef,
360 ) -> Self {
361 Self {
362 start: Instant::now(),
363 stmt,
364 schema_name,
365 threshold,
366 sample_ratio,
367 record_type,
368 recorder,
369 record_state: Arc::default(),
370 }
371 }
372
373 pub fn recorder(&self) -> SlowQueryRecorder {
375 SlowQueryRecorder {
376 record_state: self.record_state.clone(),
377 }
378 }
379}
380
381impl SlowQueryTimer {
382 fn send_slow_query_event(&self, elapsed: Duration, payload: serde_json::Value) {
383 let mut slow_query_event = SlowQueryEvent {
384 cost: elapsed.as_millis() as u64,
385 threshold: self.threshold.as_millis() as u64,
386 query: "".to_string(),
387 schema_name: self.schema_name.clone(),
388
389 is_promql: false,
391 promql_range: None,
392 promql_step: None,
393 promql_start: None,
394 promql_end: None,
395 payload,
396 };
397
398 match &self.stmt {
399 QueryStatement::Promql(stmt, _alias) => {
400 slow_query_event.is_promql = true;
401 slow_query_event.query = self.stmt.to_string();
402 slow_query_event.promql_step = Some(stmt.interval.as_millis() as u64);
403
404 let start = stmt
405 .start
406 .duration_since(UNIX_EPOCH)
407 .unwrap_or_default()
408 .as_millis() as i64;
409
410 let end = stmt
411 .end
412 .duration_since(UNIX_EPOCH)
413 .unwrap_or_default()
414 .as_millis() as i64;
415
416 slow_query_event.promql_range = Some((end - start) as u64);
417 slow_query_event.promql_start = Some(start);
418 slow_query_event.promql_end = Some(end);
419 }
420 QueryStatement::Sql(stmt) => {
421 slow_query_event.query = stmt.to_string();
422 }
423 QueryStatement::Plan(query) => {
424 slow_query_event.query = query.clone();
425 }
426 }
427
428 match self.record_type {
429 SlowQueriesRecordType::SystemTable => {
431 self.recorder.record(Box::new(slow_query_event));
432 }
433 SlowQueriesRecordType::Log => {
435 slow!(
436 cost = slow_query_event.cost,
437 threshold = slow_query_event.threshold,
438 query = slow_query_event.query,
439 schema_name = slow_query_event.schema_name,
440 is_promql = slow_query_event.is_promql,
441 promql_range = slow_query_event.promql_range,
442 promql_step = slow_query_event.promql_step,
443 promql_start = slow_query_event.promql_start,
444 promql_end = slow_query_event.promql_end,
445 payload = slow_query_event.payload.to_string(),
446 );
447 }
448 }
449 }
450}
451
452impl Drop for SlowQueryTimer {
453 fn drop(&mut self) {
454 let elapsed = self.start.elapsed();
455 let (force_record, payload) = {
456 let state = self.record_state.read().unwrap();
457 (state.force_record, state.payload.clone())
458 };
459
460 if force_record
461 || (elapsed > self.threshold
462 && (self.sample_ratio >= 1.0 || random::<f64>() <= self.sample_ratio))
463 {
464 self.send_slow_query_event(elapsed, payload);
465 }
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use std::sync::{Arc, Mutex};
472 use std::time::Duration;
473
474 use common_event_recorder::{Event, EventRecorder, EventTypeFilter, EventTypeFilterRef};
475 use common_frontend::slow_query_event::SlowQueryEvent;
476 use common_telemetry::logging::SlowQueriesRecordType;
477 use serde_json::Value;
478
479 use crate::process_manager::{ProcessManager, QueryStatement, SlowQueryTimer};
480
481 #[derive(Debug, Default)]
482 struct RecordingEventRecorder {
483 events: Mutex<Vec<(String, Value)>>,
484 }
485
486 impl EventRecorder for RecordingEventRecorder {
487 fn record(&self, event: Box<dyn Event>) {
488 let event = event
489 .as_any()
490 .downcast_ref::<SlowQueryEvent>()
491 .expect("expected a slow query event");
492 self.events
493 .lock()
494 .unwrap()
495 .push((event.query.clone(), event.payload.clone()));
496 }
497
498 fn event_type_filter(&self) -> EventTypeFilterRef {
499 Arc::new(EventTypeFilter::All)
500 }
501
502 fn close(&self) {}
503 }
504
505 #[test]
506 fn test_forced_slow_query_bypasses_threshold_and_sampling() {
507 let event_recorder = Arc::new(RecordingEventRecorder::default());
508 let timer = SlowQueryTimer::new(
509 QueryStatement::Plan("EXPLAIN ANALYZE VERBOSE SELECT 1".to_string()),
510 "public".to_string(),
511 Duration::from_secs(3600),
512 0.0,
513 SlowQueriesRecordType::SystemTable,
514 event_recorder.clone(),
515 );
516 let payload = serde_json::json!({
517 "timed_out": true,
518 "metrics": [{"stage": 0}],
519 });
520 timer.recorder().force_record_with_payload(payload.clone());
521
522 drop(timer);
523
524 let events = event_recorder.events.lock().unwrap();
525 assert_eq!(events.len(), 1);
526 assert_eq!(events[0].0, "EXPLAIN ANALYZE VERBOSE SELECT 1");
527 assert_eq!(events[0].1, payload);
528 }
529
530 #[test]
531 fn test_unforced_fast_query_is_not_recorded() {
532 let event_recorder = Arc::new(RecordingEventRecorder::default());
533 let timer = SlowQueryTimer::new(
534 QueryStatement::Plan("SELECT 1".to_string()),
535 "public".to_string(),
536 Duration::from_secs(3600),
537 0.0,
538 SlowQueriesRecordType::SystemTable,
539 event_recorder.clone(),
540 );
541
542 drop(timer);
543
544 assert!(event_recorder.events.lock().unwrap().is_empty());
545 }
546
547 #[tokio::test]
548 async fn test_register_query() {
549 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
550 let ticket = process_manager.clone().register_query(
551 "public".to_string(),
552 vec!["test".to_string()],
553 "SELECT * FROM table".to_string(),
554 "".to_string(),
555 None,
556 None,
557 );
558
559 let running_processes = process_manager.local_processes(None).unwrap();
560 assert_eq!(running_processes.len(), 1);
561 assert_eq!(&running_processes[0].frontend, "127.0.0.1:8000");
562 assert_eq!(running_processes[0].id, ticket.id);
563 assert_eq!(&running_processes[0].query, "SELECT * FROM table");
564
565 drop(ticket);
566 assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
567 }
568
569 #[tokio::test]
570 async fn test_register_query_with_custom_id() {
571 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
572 let custom_id = 12345;
573
574 let ticket = process_manager.clone().register_query(
575 "public".to_string(),
576 vec!["test".to_string()],
577 "SELECT * FROM table".to_string(),
578 "client1".to_string(),
579 Some(custom_id),
580 None,
581 );
582
583 assert_eq!(ticket.id, custom_id);
584
585 let running_processes = process_manager.local_processes(None).unwrap();
586 assert_eq!(running_processes.len(), 1);
587 assert_eq!(running_processes[0].id, custom_id);
588 assert_eq!(&running_processes[0].client, "client1");
589 }
590
591 #[tokio::test]
592 async fn test_multiple_queries_same_catalog() {
593 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
594
595 let ticket1 = process_manager.clone().register_query(
596 "public".to_string(),
597 vec!["schema1".to_string()],
598 "SELECT * FROM table1".to_string(),
599 "client1".to_string(),
600 None,
601 None,
602 );
603
604 let ticket2 = process_manager.clone().register_query(
605 "public".to_string(),
606 vec!["schema2".to_string()],
607 "SELECT * FROM table2".to_string(),
608 "client2".to_string(),
609 None,
610 None,
611 );
612
613 let running_processes = process_manager.local_processes(Some("public")).unwrap();
614 assert_eq!(running_processes.len(), 2);
615
616 let ids: Vec<u32> = running_processes.iter().map(|p| p.id).collect();
618 assert!(ids.contains(&ticket1.id));
619 assert!(ids.contains(&ticket2.id));
620 }
621
622 #[tokio::test]
623 async fn test_multiple_catalogs() {
624 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
625
626 let _ticket1 = process_manager.clone().register_query(
627 "catalog1".to_string(),
628 vec!["schema1".to_string()],
629 "SELECT * FROM table1".to_string(),
630 "client1".to_string(),
631 None,
632 None,
633 );
634
635 let _ticket2 = process_manager.clone().register_query(
636 "catalog2".to_string(),
637 vec!["schema2".to_string()],
638 "SELECT * FROM table2".to_string(),
639 "client2".to_string(),
640 None,
641 None,
642 );
643
644 let catalog1_processes = process_manager.local_processes(Some("catalog1")).unwrap();
646 assert_eq!(catalog1_processes.len(), 1);
647 assert_eq!(&catalog1_processes[0].catalog, "catalog1");
648
649 let catalog2_processes = process_manager.local_processes(Some("catalog2")).unwrap();
650 assert_eq!(catalog2_processes.len(), 1);
651 assert_eq!(&catalog2_processes[0].catalog, "catalog2");
652
653 let all_processes = process_manager.local_processes(None).unwrap();
655 assert_eq!(all_processes.len(), 2);
656 }
657
658 #[tokio::test]
659 async fn test_deregister_query() {
660 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
661
662 let ticket = process_manager.clone().register_query(
663 "public".to_string(),
664 vec!["test".to_string()],
665 "SELECT * FROM table".to_string(),
666 "client1".to_string(),
667 None,
668 None,
669 );
670 assert_eq!(process_manager.local_processes(None).unwrap().len(), 1);
671 process_manager.deregister_query("public".to_string(), ticket.id);
672 assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
673 }
674
675 #[tokio::test]
676 async fn test_cancellation_handle() {
677 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
678
679 let ticket = process_manager.clone().register_query(
680 "public".to_string(),
681 vec!["test".to_string()],
682 "SELECT * FROM table".to_string(),
683 "client1".to_string(),
684 None,
685 None,
686 );
687
688 assert!(!ticket.cancellation_handle.is_cancelled());
689 ticket.cancellation_handle.cancel();
690 assert!(ticket.cancellation_handle.is_cancelled());
691 }
692
693 #[tokio::test]
694 async fn test_kill_local_process() {
695 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
696
697 let ticket = process_manager.clone().register_query(
698 "public".to_string(),
699 vec!["test".to_string()],
700 "SELECT * FROM table".to_string(),
701 "client1".to_string(),
702 None,
703 None,
704 );
705 assert!(!ticket.cancellation_handle.is_cancelled());
706 let killed = process_manager
707 .kill_process(
708 "127.0.0.1:8000".to_string(),
709 "public".to_string(),
710 ticket.id,
711 )
712 .await
713 .unwrap();
714
715 assert!(killed);
716 assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
717 }
718
719 #[tokio::test]
720 async fn test_kill_nonexistent_process() {
721 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
722 let killed = process_manager
723 .kill_process("127.0.0.1:8000".to_string(), "public".to_string(), 999)
724 .await
725 .unwrap();
726 assert!(!killed);
727 }
728
729 #[tokio::test]
730 async fn test_kill_process_nonexistent_catalog() {
731 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
732 let killed = process_manager
733 .kill_process("127.0.0.1:8000".to_string(), "nonexistent".to_string(), 1)
734 .await
735 .unwrap();
736 assert!(!killed);
737 }
738
739 #[tokio::test]
740 async fn test_process_info_fields() {
741 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
742
743 let _ticket = process_manager.clone().register_query(
744 "test_catalog".to_string(),
745 vec!["schema1".to_string(), "schema2".to_string()],
746 "SELECT COUNT(*) FROM users WHERE age > 18".to_string(),
747 "test_client".to_string(),
748 Some(42),
749 None,
750 );
751
752 let processes = process_manager.local_processes(None).unwrap();
753 assert_eq!(processes.len(), 1);
754
755 let process = &processes[0];
756 assert_eq!(process.id, 42);
757 assert_eq!(&process.catalog, "test_catalog");
758 assert_eq!(process.schemas, vec!["schema1", "schema2"]);
759 assert_eq!(&process.query, "SELECT COUNT(*) FROM users WHERE age > 18");
760 assert_eq!(&process.client, "test_client");
761 assert_eq!(&process.frontend, "127.0.0.1:8000");
762 assert!(process.start_timestamp > 0);
763 }
764
765 #[tokio::test]
766 async fn test_ticket_drop_deregisters_process() {
767 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
768
769 {
770 let _ticket = process_manager.clone().register_query(
771 "public".to_string(),
772 vec!["test".to_string()],
773 "SELECT * FROM table".to_string(),
774 "client1".to_string(),
775 None,
776 None,
777 );
778
779 assert_eq!(process_manager.local_processes(None).unwrap().len(), 1);
781 } assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
785 }
786}