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, warn};
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 server_addr: String,
46 next_id: AtomicU32,
48 catalogs: RwLock<HashMap<String, HashMap<ProcessId, CancellableProcess>>>,
50 frontend_selector: Option<MetaClientSelector>,
52}
53
54#[derive(Debug, Clone)]
57pub enum QueryStatement {
58 Sql(Statement),
59 Promql(EvalStmt, Option<String>),
61 Plan(String),
63}
64
65impl Display for QueryStatement {
66 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
67 match self {
68 QueryStatement::Sql(stmt) => write!(f, "{}", stmt),
69 QueryStatement::Promql(eval_stmt, alias) => {
70 if let Some(alias) = alias {
71 write!(f, "{} AS {}", eval_stmt, alias)
72 } else {
73 write!(f, "{}", eval_stmt)
74 }
75 }
76 QueryStatement::Plan(query) => write!(f, "{}", query),
77 }
78 }
79}
80
81impl ProcessManager {
82 pub fn new(server_addr: String, meta_client: Option<MetaClientRef>) -> Self {
84 let frontend_selector = meta_client.map(MetaClientSelector::new);
85 Self {
86 server_addr,
87 next_id: Default::default(),
88 catalogs: Default::default(),
89 frontend_selector,
90 }
91 }
92}
93
94impl ProcessManager {
95 #[must_use]
97 pub fn register_query(
98 self: &Arc<Self>,
99 catalog: String,
100 schemas: Vec<String>,
101 query: String,
102 client: String,
103 query_id: Option<ProcessId>,
104 _slow_query_timer: Option<SlowQueryTimer>,
105 ) -> Ticket {
106 let id = query_id.unwrap_or_else(|| self.next_id.fetch_add(1, Ordering::Relaxed));
107 let process = ProcessInfo {
108 id,
109 catalog: catalog.clone(),
110 schemas,
111 query,
112 start_timestamp: current_time_millis(),
113 client,
114 frontend: self.server_addr.clone(),
115 };
116 let cancellation_handle = Arc::new(CancellationHandle::default());
117 let cancellable_process = CancellableProcess::new(cancellation_handle.clone(), process);
118
119 self.catalogs
120 .write()
121 .unwrap()
122 .entry(catalog.clone())
123 .or_default()
124 .insert(id, cancellable_process);
125
126 Ticket {
127 catalog,
128 manager: self.clone(),
129 id,
130 cancellation_handle,
131 _slow_query_timer,
132 }
133 }
134
135 pub fn next_id(&self) -> u32 {
137 self.next_id.fetch_add(1, Ordering::Relaxed)
138 }
139
140 pub fn deregister_query(&self, catalog: String, id: ProcessId) {
142 if let Entry::Occupied(mut o) = self.catalogs.write().unwrap().entry(catalog) {
143 let process = o.get_mut().remove(&id);
144 debug!("Deregister process: {:?}", process);
145 if o.get().is_empty() {
146 o.remove();
147 }
148 }
149 }
150
151 pub fn local_processes(&self, catalog: Option<&str>) -> error::Result<Vec<ProcessInfo>> {
153 let catalogs = self.catalogs.read().unwrap();
154 let result = if let Some(catalog) = catalog {
155 if let Some(catalogs) = catalogs.get(catalog) {
156 catalogs.values().map(|p| p.process.clone()).collect()
157 } else {
158 vec![]
159 }
160 } else {
161 catalogs
162 .values()
163 .flat_map(|v| v.values().map(|p| p.process.clone()))
164 .collect()
165 };
166 Ok(result)
167 }
168
169 pub async fn list_all_processes(
170 &self,
171 catalog: Option<&str>,
172 ) -> error::Result<Vec<ProcessInfo>> {
173 let mut processes = vec![];
174 if let Some(remote_frontend_selector) = self.frontend_selector.as_ref() {
175 let frontends = remote_frontend_selector
176 .select(|peer| peer.addr != self.server_addr)
177 .await
178 .context(error::InvokeFrontendSnafu)?;
179 for mut f in frontends {
180 let result = f
181 .list_process(ListProcessRequest {
182 catalog: catalog.unwrap_or_default().to_string(),
183 })
184 .await
185 .context(error::InvokeFrontendSnafu);
186 match result {
187 Ok(resp) => {
188 processes.extend(resp.processes);
189 }
190 Err(e) => {
191 warn!(e; "Skipping failing node: {:?}", f)
192 }
193 }
194 }
195 }
196 processes.extend(self.local_processes(catalog)?);
197 Ok(processes)
198 }
199
200 pub async fn kill_process(
202 &self,
203 server_addr: String,
204 catalog: String,
205 id: ProcessId,
206 ) -> error::Result<bool> {
207 if server_addr == self.server_addr {
208 self.kill_local_process(catalog, id).await
209 } else {
210 let mut nodes = self
211 .frontend_selector
212 .as_ref()
213 .context(error::MetaClientMissingSnafu)?
214 .select(|peer| peer.addr == server_addr)
215 .await
216 .context(error::InvokeFrontendSnafu)?;
217 ensure!(
218 !nodes.is_empty(),
219 error::FrontendNotFoundSnafu { addr: server_addr }
220 );
221
222 let request = KillProcessRequest {
223 server_addr,
224 catalog,
225 process_id: id,
226 };
227 nodes[0]
228 .kill_process(request)
229 .await
230 .context(error::InvokeFrontendSnafu)?;
231 Ok(true)
232 }
233 }
234
235 pub async fn kill_local_process(&self, catalog: String, id: ProcessId) -> error::Result<bool> {
237 if let Some(catalogs) = self.catalogs.write().unwrap().get_mut(&catalog) {
238 if let Some(process) = catalogs.remove(&id) {
239 process.handle.cancel();
240 info!(
241 "Killed process, catalog: {}, id: {:?}",
242 process.process.catalog, process.process.id
243 );
244 PROCESS_KILL_COUNT.with_label_values(&[&catalog]).inc();
245 Ok(true)
246 } else {
247 debug!("Failed to kill process, id not found: {}", id);
248 Ok(false)
249 }
250 } else {
251 debug!("Failed to kill process, catalog not found: {}", catalog);
252 Ok(false)
253 }
254 }
255}
256
257pub struct Ticket {
258 pub(crate) catalog: String,
259 pub(crate) manager: ProcessManagerRef,
260 pub(crate) id: ProcessId,
261 pub cancellation_handle: Arc<CancellationHandle>,
262
263 _slow_query_timer: Option<SlowQueryTimer>,
265}
266
267impl Drop for Ticket {
268 fn drop(&mut self) {
269 self.manager
270 .deregister_query(std::mem::take(&mut self.catalog), self.id);
271 }
272}
273
274struct CancellableProcess {
275 handle: Arc<CancellationHandle>,
276 process: ProcessInfo,
277}
278
279impl Drop for CancellableProcess {
280 fn drop(&mut self) {
281 PROCESS_LIST_COUNT
282 .with_label_values(&[&self.process.catalog])
283 .dec();
284 }
285}
286
287impl CancellableProcess {
288 fn new(handle: Arc<CancellationHandle>, process: ProcessInfo) -> Self {
289 PROCESS_LIST_COUNT
290 .with_label_values(&[&process.catalog])
291 .inc();
292 Self { handle, process }
293 }
294}
295
296impl Debug for CancellableProcess {
297 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
298 f.debug_struct("CancellableProcess")
299 .field("cancelled", &self.handle.is_cancelled())
300 .field("process", &self.process)
301 .finish()
302 }
303}
304
305pub struct SlowQueryTimer {
308 start: Instant,
309 stmt: QueryStatement,
310 schema_name: String,
311 threshold: Duration,
312 sample_ratio: f64,
313 record_type: SlowQueriesRecordType,
314 recorder: EventRecorderRef,
315}
316
317impl SlowQueryTimer {
318 pub fn new(
319 stmt: QueryStatement,
320 schema_name: String,
321 threshold: Duration,
322 sample_ratio: f64,
323 record_type: SlowQueriesRecordType,
324 recorder: EventRecorderRef,
325 ) -> Self {
326 Self {
327 start: Instant::now(),
328 stmt,
329 schema_name,
330 threshold,
331 sample_ratio,
332 record_type,
333 recorder,
334 }
335 }
336}
337
338impl SlowQueryTimer {
339 fn send_slow_query_event(&self, elapsed: Duration) {
340 let mut slow_query_event = SlowQueryEvent {
341 cost: elapsed.as_millis() as u64,
342 threshold: self.threshold.as_millis() as u64,
343 query: "".to_string(),
344 schema_name: self.schema_name.clone(),
345
346 is_promql: false,
348 promql_range: None,
349 promql_step: None,
350 promql_start: None,
351 promql_end: None,
352 };
353
354 match &self.stmt {
355 QueryStatement::Promql(stmt, _alias) => {
356 slow_query_event.is_promql = true;
357 slow_query_event.query = self.stmt.to_string();
358 slow_query_event.promql_step = Some(stmt.interval.as_millis() as u64);
359
360 let start = stmt
361 .start
362 .duration_since(UNIX_EPOCH)
363 .unwrap_or_default()
364 .as_millis() as i64;
365
366 let end = stmt
367 .end
368 .duration_since(UNIX_EPOCH)
369 .unwrap_or_default()
370 .as_millis() as i64;
371
372 slow_query_event.promql_range = Some((end - start) as u64);
373 slow_query_event.promql_start = Some(start);
374 slow_query_event.promql_end = Some(end);
375 }
376 QueryStatement::Sql(stmt) => {
377 slow_query_event.query = stmt.to_string();
378 }
379 QueryStatement::Plan(query) => {
380 slow_query_event.query = query.clone();
381 }
382 }
383
384 match self.record_type {
385 SlowQueriesRecordType::SystemTable => {
387 self.recorder.record(Box::new(slow_query_event));
388 }
389 SlowQueriesRecordType::Log => {
391 slow!(
392 cost = slow_query_event.cost,
393 threshold = slow_query_event.threshold,
394 query = slow_query_event.query,
395 schema_name = slow_query_event.schema_name,
396 is_promql = slow_query_event.is_promql,
397 promql_range = slow_query_event.promql_range,
398 promql_step = slow_query_event.promql_step,
399 promql_start = slow_query_event.promql_start,
400 promql_end = slow_query_event.promql_end,
401 );
402 }
403 }
404 }
405}
406
407impl Drop for SlowQueryTimer {
408 fn drop(&mut self) {
409 let elapsed = self.start.elapsed();
411 if elapsed > self.threshold {
412 if self.sample_ratio >= 1.0 || random::<f64>() <= self.sample_ratio {
415 self.send_slow_query_event(elapsed);
416 }
417 }
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use std::sync::Arc;
424
425 use crate::process_manager::ProcessManager;
426
427 #[tokio::test]
428 async fn test_register_query() {
429 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
430 let ticket = process_manager.clone().register_query(
431 "public".to_string(),
432 vec!["test".to_string()],
433 "SELECT * FROM table".to_string(),
434 "".to_string(),
435 None,
436 None,
437 );
438
439 let running_processes = process_manager.local_processes(None).unwrap();
440 assert_eq!(running_processes.len(), 1);
441 assert_eq!(&running_processes[0].frontend, "127.0.0.1:8000");
442 assert_eq!(running_processes[0].id, ticket.id);
443 assert_eq!(&running_processes[0].query, "SELECT * FROM table");
444
445 drop(ticket);
446 assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
447 }
448
449 #[tokio::test]
450 async fn test_register_query_with_custom_id() {
451 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
452 let custom_id = 12345;
453
454 let ticket = process_manager.clone().register_query(
455 "public".to_string(),
456 vec!["test".to_string()],
457 "SELECT * FROM table".to_string(),
458 "client1".to_string(),
459 Some(custom_id),
460 None,
461 );
462
463 assert_eq!(ticket.id, custom_id);
464
465 let running_processes = process_manager.local_processes(None).unwrap();
466 assert_eq!(running_processes.len(), 1);
467 assert_eq!(running_processes[0].id, custom_id);
468 assert_eq!(&running_processes[0].client, "client1");
469 }
470
471 #[tokio::test]
472 async fn test_multiple_queries_same_catalog() {
473 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
474
475 let ticket1 = process_manager.clone().register_query(
476 "public".to_string(),
477 vec!["schema1".to_string()],
478 "SELECT * FROM table1".to_string(),
479 "client1".to_string(),
480 None,
481 None,
482 );
483
484 let ticket2 = process_manager.clone().register_query(
485 "public".to_string(),
486 vec!["schema2".to_string()],
487 "SELECT * FROM table2".to_string(),
488 "client2".to_string(),
489 None,
490 None,
491 );
492
493 let running_processes = process_manager.local_processes(Some("public")).unwrap();
494 assert_eq!(running_processes.len(), 2);
495
496 let ids: Vec<u32> = running_processes.iter().map(|p| p.id).collect();
498 assert!(ids.contains(&ticket1.id));
499 assert!(ids.contains(&ticket2.id));
500 }
501
502 #[tokio::test]
503 async fn test_multiple_catalogs() {
504 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
505
506 let _ticket1 = process_manager.clone().register_query(
507 "catalog1".to_string(),
508 vec!["schema1".to_string()],
509 "SELECT * FROM table1".to_string(),
510 "client1".to_string(),
511 None,
512 None,
513 );
514
515 let _ticket2 = process_manager.clone().register_query(
516 "catalog2".to_string(),
517 vec!["schema2".to_string()],
518 "SELECT * FROM table2".to_string(),
519 "client2".to_string(),
520 None,
521 None,
522 );
523
524 let catalog1_processes = process_manager.local_processes(Some("catalog1")).unwrap();
526 assert_eq!(catalog1_processes.len(), 1);
527 assert_eq!(&catalog1_processes[0].catalog, "catalog1");
528
529 let catalog2_processes = process_manager.local_processes(Some("catalog2")).unwrap();
530 assert_eq!(catalog2_processes.len(), 1);
531 assert_eq!(&catalog2_processes[0].catalog, "catalog2");
532
533 let all_processes = process_manager.local_processes(None).unwrap();
535 assert_eq!(all_processes.len(), 2);
536 }
537
538 #[tokio::test]
539 async fn test_deregister_query() {
540 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
541
542 let ticket = process_manager.clone().register_query(
543 "public".to_string(),
544 vec!["test".to_string()],
545 "SELECT * FROM table".to_string(),
546 "client1".to_string(),
547 None,
548 None,
549 );
550 assert_eq!(process_manager.local_processes(None).unwrap().len(), 1);
551 process_manager.deregister_query("public".to_string(), ticket.id);
552 assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
553 }
554
555 #[tokio::test]
556 async fn test_cancellation_handle() {
557 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
558
559 let ticket = process_manager.clone().register_query(
560 "public".to_string(),
561 vec!["test".to_string()],
562 "SELECT * FROM table".to_string(),
563 "client1".to_string(),
564 None,
565 None,
566 );
567
568 assert!(!ticket.cancellation_handle.is_cancelled());
569 ticket.cancellation_handle.cancel();
570 assert!(ticket.cancellation_handle.is_cancelled());
571 }
572
573 #[tokio::test]
574 async fn test_kill_local_process() {
575 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
576
577 let ticket = process_manager.clone().register_query(
578 "public".to_string(),
579 vec!["test".to_string()],
580 "SELECT * FROM table".to_string(),
581 "client1".to_string(),
582 None,
583 None,
584 );
585 assert!(!ticket.cancellation_handle.is_cancelled());
586 let killed = process_manager
587 .kill_process(
588 "127.0.0.1:8000".to_string(),
589 "public".to_string(),
590 ticket.id,
591 )
592 .await
593 .unwrap();
594
595 assert!(killed);
596 assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
597 }
598
599 #[tokio::test]
600 async fn test_kill_nonexistent_process() {
601 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
602 let killed = process_manager
603 .kill_process("127.0.0.1:8000".to_string(), "public".to_string(), 999)
604 .await
605 .unwrap();
606 assert!(!killed);
607 }
608
609 #[tokio::test]
610 async fn test_kill_process_nonexistent_catalog() {
611 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
612 let killed = process_manager
613 .kill_process("127.0.0.1:8000".to_string(), "nonexistent".to_string(), 1)
614 .await
615 .unwrap();
616 assert!(!killed);
617 }
618
619 #[tokio::test]
620 async fn test_process_info_fields() {
621 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
622
623 let _ticket = process_manager.clone().register_query(
624 "test_catalog".to_string(),
625 vec!["schema1".to_string(), "schema2".to_string()],
626 "SELECT COUNT(*) FROM users WHERE age > 18".to_string(),
627 "test_client".to_string(),
628 Some(42),
629 None,
630 );
631
632 let processes = process_manager.local_processes(None).unwrap();
633 assert_eq!(processes.len(), 1);
634
635 let process = &processes[0];
636 assert_eq!(process.id, 42);
637 assert_eq!(&process.catalog, "test_catalog");
638 assert_eq!(process.schemas, vec!["schema1", "schema2"]);
639 assert_eq!(&process.query, "SELECT COUNT(*) FROM users WHERE age > 18");
640 assert_eq!(&process.client, "test_client");
641 assert_eq!(&process.frontend, "127.0.0.1:8000");
642 assert!(process.start_timestamp > 0);
643 }
644
645 #[tokio::test]
646 async fn test_ticket_drop_deregisters_process() {
647 let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
648
649 {
650 let _ticket = process_manager.clone().register_query(
651 "public".to_string(),
652 vec!["test".to_string()],
653 "SELECT * FROM table".to_string(),
654 "client1".to_string(),
655 None,
656 None,
657 );
658
659 assert_eq!(process_manager.local_processes(None).unwrap().len(), 1);
661 } assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
665 }
666}