1use std::collections::{BTreeMap, HashMap, HashSet};
16use std::sync::Arc;
17use std::time::Duration;
18
19use foyer::{HybridCache, TracingOptions};
20use prometheus::core::Collector;
21use prometheus::proto::Metric;
22use risingwave_batch::task::BatchManager;
23use risingwave_batch::task::await_tree_key::BatchTask;
24use risingwave_common::bitmap::Bitmap;
25use risingwave_common::config::{MetricLevel, ServerConfig};
26use risingwave_common_heap_profiling::ProfileServiceImpl;
27use risingwave_hummock_sdk::HummockSstableObjectId;
28use risingwave_jni_core::jvm_runtime::dump_jvm_stack_traces;
29use risingwave_pb::monitor_service::monitor_service_server::MonitorService;
30use risingwave_pb::monitor_service::stack_trace_request::ActorTracesFormat;
31use risingwave_pb::monitor_service::{
32 AnalyzeHeapRequest, AnalyzeHeapResponse, ChannelStats, FragmentStats, GetProfileStatsRequest,
33 GetProfileStatsResponse, GetStreamingStatsRequest, GetStreamingStatsResponse,
34 GetTableCacheRefillStatsRequest, GetTableCacheRefillStatsResponse, HeapProfilingRequest,
35 HeapProfilingResponse, ListHeapProfilingRequest, ListHeapProfilingResponse, ProfilingRequest,
36 ProfilingResponse, RelationStats, StackTraceRequest, StackTraceResponse,
37 TieredCacheTracingRequest, TieredCacheTracingResponse,
38};
39use risingwave_storage::hummock::compactor::await_tree_key::Compaction;
40use risingwave_storage::hummock::event_handler::refiller::{
41 TableCacheRefillContext, TableCacheRefillMonitorSnapshot,
42};
43use risingwave_storage::hummock::store::HummockStorage;
44use risingwave_storage::hummock::{Block, Sstable, SstableBlockIndex};
45use risingwave_stream::executor::monitor::global_streaming_metrics;
46use risingwave_stream::task::LocalStreamManager;
47use risingwave_stream::task::await_tree_key::{Actor, BarrierAwait};
48use thiserror_ext::AsReport;
49use tonic::{Request, Response, Status};
50
51type MetaCache = HybridCache<HummockSstableObjectId, Box<Sstable>>;
52type BlockCache = HybridCache<SstableBlockIndex, Box<Block>>;
53
54#[derive(Clone)]
55pub struct MonitorServiceImpl {
56 stream_mgr: LocalStreamManager,
57 batch_mgr: Arc<BatchManager>,
58 profile_service: ProfileServiceImpl,
59 meta_cache: Option<MetaCache>,
60 block_cache: Option<BlockCache>,
61 hummock_storage: Option<HummockStorage>,
62}
63
64impl MonitorServiceImpl {
65 pub fn new(
66 stream_mgr: LocalStreamManager,
67 batch_mgr: Arc<BatchManager>,
68 server_config: ServerConfig,
69 meta_cache: Option<MetaCache>,
70 block_cache: Option<BlockCache>,
71 hummock_storage: Option<HummockStorage>,
72 ) -> Self {
73 Self {
74 stream_mgr,
75 batch_mgr,
76 profile_service: ProfileServiceImpl::new(server_config),
77 meta_cache,
78 block_cache,
79 hummock_storage,
80 }
81 }
82}
83
84#[async_trait::async_trait]
85impl MonitorService for MonitorServiceImpl {
86 async fn stack_trace(
87 &self,
88 request: Request<StackTraceRequest>,
89 ) -> Result<Response<StackTraceResponse>, Status> {
90 let req = request.into_inner();
91
92 let actor_traces = if let Some(reg) = self.stream_mgr.await_tree_reg() {
93 reg.collect::<Actor>()
94 .into_iter()
95 .map(|(k, v)| {
96 (
97 k.0.as_raw_id(),
98 if req.actor_traces_format == ActorTracesFormat::Text as i32 {
99 v.to_string()
100 } else {
101 serde_json::to_string(&v).unwrap()
102 },
103 )
104 })
105 .collect()
106 } else {
107 Default::default()
108 };
109
110 let barrier_traces = if let Some(reg) = self.stream_mgr.await_tree_reg() {
111 reg.collect::<BarrierAwait>()
112 .into_iter()
113 .map(|(k, v)| (k.prev_epoch, v.to_string()))
114 .collect()
115 } else {
116 Default::default()
117 };
118
119 let mut rpc_traces: BTreeMap<_, _> = if let Some(reg) = self.stream_mgr.await_tree_reg() {
120 reg.collect::<GrpcCall>()
121 .into_iter()
122 .map(|(k, v)| (k.desc, v.to_string()))
123 .collect()
124 } else {
125 Default::default()
126 };
127
128 if let Some(reg) = self.batch_mgr.await_tree_reg() {
129 rpc_traces.extend(
130 reg.collect::<GrpcCall>()
131 .into_iter()
132 .map(|(k, v)| (k.desc, v.to_string())),
133 );
134 }
135
136 let batch_traces = if let Some(reg) = self.batch_mgr.await_tree_reg() {
137 reg.collect::<BatchTask>()
138 .into_iter()
139 .map(|(k, v)| {
140 let id = &k.0;
141 (
142 format!("{}-{}-{}", id.query_id, id.stage_id, id.task_id),
143 v.to_string(),
144 )
145 })
146 .collect()
147 } else {
148 Default::default()
149 };
150
151 let compaction_task_traces = if let Some(hummock) =
152 self.stream_mgr.env.state_store().as_hummock()
153 && let Some(m) = hummock.compaction_await_tree_reg()
154 {
155 m.collect::<Compaction>()
156 .into_iter()
157 .map(|(k, v)| (format!("{k:?}"), v.to_string()))
158 .collect()
159 } else {
160 Default::default()
161 };
162
163 let barrier_worker_state = self.stream_mgr.inspect_barrier_state().await?;
164
165 let jvm_stack_traces = match dump_jvm_stack_traces() {
166 Ok(None) => None,
167 Err(err) => Some(err.as_report().to_string()),
168 Ok(Some(stack_traces)) => Some(stack_traces),
169 };
170
171 Ok(Response::new(StackTraceResponse {
172 actor_traces,
173 rpc_traces,
174 compaction_task_traces,
175 inflight_barrier_traces: barrier_traces,
176 barrier_worker_state: BTreeMap::from_iter([(
177 self.stream_mgr.env.worker_id(),
178 barrier_worker_state,
179 )]),
180 jvm_stack_traces: match jvm_stack_traces {
181 Some(stack_traces) => {
182 BTreeMap::from_iter([(self.stream_mgr.env.worker_id(), stack_traces)])
183 }
184 None => BTreeMap::new(),
185 },
186 meta_traces: Default::default(),
187 node_errors: Default::default(),
188 batch_traces,
189 }))
190 }
191
192 async fn profiling(
193 &self,
194 request: Request<ProfilingRequest>,
195 ) -> Result<Response<ProfilingResponse>, Status> {
196 self.profile_service.profiling(request).await
197 }
198
199 async fn heap_profiling(
200 &self,
201 request: Request<HeapProfilingRequest>,
202 ) -> Result<Response<HeapProfilingResponse>, Status> {
203 self.profile_service.heap_profiling(request)
204 }
205
206 async fn list_heap_profiling(
207 &self,
208 _request: Request<ListHeapProfilingRequest>,
209 ) -> Result<Response<ListHeapProfilingResponse>, Status> {
210 self.profile_service.list_heap_profiling(_request)
211 }
212
213 async fn analyze_heap(
214 &self,
215 request: Request<AnalyzeHeapRequest>,
216 ) -> Result<Response<AnalyzeHeapResponse>, Status> {
217 self.profile_service.analyze_heap(request).await
218 }
219
220 async fn get_profile_stats(
221 &self,
222 request: Request<GetProfileStatsRequest>,
223 ) -> Result<Response<GetProfileStatsResponse>, Status> {
224 let metrics = global_streaming_metrics(MetricLevel::Info);
225 let inner = request.into_inner();
226 let executor_ids = &inner.executor_ids;
227 let fragment_ids = HashSet::from_iter(inner.dispatcher_fragment_ids);
228 let stream_node_output_row_count = metrics
229 .mem_stream_node_output_row_count
230 .collect(executor_ids);
231 let stream_node_output_blocking_duration_ns = metrics
232 .mem_stream_node_output_blocking_duration_ns
233 .collect(executor_ids);
234
235 fn collect_by_fragment_ids<T: Collector>(
237 m: &T,
238 fragment_ids: &HashSet<FragmentId>,
239 ) -> HashMap<FragmentId, u64> {
240 let mut metrics = HashMap::new();
241 for mut metric_family in m.collect() {
242 for metric in metric_family.take_metric() {
243 let fragment_id = get_label_infallible(&metric, "fragment_id");
244 if fragment_ids.contains(&fragment_id) {
245 let entry = metrics.entry(fragment_id).or_insert(0);
246 *entry += metric.get_counter().value() as u64;
247 }
248 }
249 }
250 metrics
251 }
252
253 let dispatch_fragment_output_row_count =
254 collect_by_fragment_ids(&metrics.actor_out_record_cnt, &fragment_ids);
255 let dispatch_fragment_output_blocking_duration_ns = collect_by_fragment_ids(
256 &metrics.actor_output_buffer_blocking_duration_ns,
257 &fragment_ids,
258 );
259 Ok(Response::new(GetProfileStatsResponse {
260 stream_node_output_row_count,
261 stream_node_output_blocking_duration_ns,
262 dispatch_fragment_output_row_count,
263 dispatch_fragment_output_blocking_duration_ns,
264 }))
265 }
266
267 async fn get_streaming_stats(
268 &self,
269 _request: Request<GetStreamingStatsRequest>,
270 ) -> Result<Response<GetStreamingStatsResponse>, Status> {
271 let metrics = global_streaming_metrics(MetricLevel::Info);
272
273 fn collect<T: Collector>(m: &T) -> Vec<Metric> {
274 m.collect().into_iter().next().unwrap().take_metric()
275 }
276
277 let actor_output_buffer_blocking_duration_ns =
278 collect(&metrics.actor_output_buffer_blocking_duration_ns);
279 let actor_count = collect(&metrics.actor_count);
280
281 let actor_count: HashMap<_, _> = actor_count
282 .iter()
283 .map(|m| {
284 let fragment_id: u32 = get_label_infallible(m, "fragment_id");
285 let count = m.get_gauge().value() as u32;
286 (fragment_id, count)
287 })
288 .collect();
289
290 let mut fragment_stats: HashMap<u32, FragmentStats> = HashMap::new();
291 for (&fragment_id, &actor_count) in &actor_count {
292 fragment_stats.insert(
293 fragment_id,
294 FragmentStats {
295 actor_count,
296 current_epoch: 0,
297 },
298 );
299 }
300
301 let actor_current_epoch = collect(&metrics.actor_current_epoch);
302 for m in &actor_current_epoch {
303 let fragment_id: u32 = get_label_infallible(m, "fragment_id");
304 let epoch = m.get_gauge().value() as u64;
305 if let Some(s) = fragment_stats.get_mut(&fragment_id) {
306 s.current_epoch = if s.current_epoch == 0 {
307 epoch
308 } else {
309 u64::min(s.current_epoch, epoch)
310 }
311 } else {
312 warn!(
313 fragment_id = fragment_id,
314 "Miss corresponding actor count metrics"
315 );
316 }
317 }
318
319 let mut relation_stats: HashMap<u32, RelationStats> = HashMap::new();
320 let mview_current_epoch = collect(&metrics.materialize_current_epoch);
321 for m in &mview_current_epoch {
322 let table_id: u32 = get_label_infallible(m, "table_id");
323 let epoch = m.get_gauge().value() as u64;
324 if let Some(s) = relation_stats.get_mut(&table_id) {
325 s.current_epoch = if s.current_epoch == 0 {
326 epoch
327 } else {
328 u64::min(s.current_epoch, epoch)
329 };
330 s.actor_count += 1;
331 } else {
332 relation_stats.insert(
333 table_id,
334 RelationStats {
335 actor_count: 1,
336 current_epoch: epoch,
337 },
338 );
339 }
340 }
341
342 let mut channel_stats: BTreeMap<String, ChannelStats> = BTreeMap::new();
343
344 for metric in actor_output_buffer_blocking_duration_ns {
345 let fragment_id: u32 = get_label_infallible(&metric, "fragment_id");
346 let downstream_fragment_id: u32 =
347 get_label_infallible(&metric, "downstream_fragment_id");
348
349 let actor_count_to_add =
350 if get_label_infallible::<String>(&metric, "actor_id").is_empty() {
351 match actor_count.get(&fragment_id) {
352 Some(&count) => count,
353 None => {
354 warn!(
357 fragment_id = fragment_id,
358 downstream_fragment_id = downstream_fragment_id,
359 "Miss corresponding actor count metrics"
360 );
361 continue;
362 }
363 }
364 } else {
365 1
366 };
367
368 let key = format!("{}_{}", fragment_id, downstream_fragment_id);
369 let channel_stat = channel_stats.entry(key).or_insert_with(|| ChannelStats {
370 actor_count: 0,
371 output_blocking_duration: 0.,
372 recv_row_count: 0,
373 send_row_count: 0,
374 });
375
376 channel_stat.actor_count += actor_count_to_add;
379 channel_stat.output_blocking_duration += metric.get_counter().value();
380 }
381
382 let actor_output_row_count = collect(&metrics.actor_out_record_cnt);
383 for metric in actor_output_row_count {
384 let fragment_id: u32 = get_label_infallible(&metric, "fragment_id");
385
386 let key_prefix = format!("{}_", fragment_id);
388 let key_range_end = format!("{}`", fragment_id); for (_, s) in channel_stats.range_mut(key_prefix..key_range_end) {
390 s.send_row_count += metric.get_counter().value() as u64;
391 }
392 }
393
394 let actor_input_row_count = collect(&metrics.actor_in_record_cnt);
395 for metric in actor_input_row_count {
396 let upstream_fragment_id: u32 = get_label_infallible(&metric, "upstream_fragment_id");
397 let fragment_id: u32 = get_label_infallible(&metric, "fragment_id");
398
399 let key = format!("{}_{}", upstream_fragment_id, fragment_id);
400 if let Some(s) = channel_stats.get_mut(&key) {
401 s.recv_row_count += metric.get_counter().value() as u64;
402 }
403 }
404
405 let channel_stats = channel_stats.into_iter().collect();
406 Ok(Response::new(GetStreamingStatsResponse {
407 channel_stats,
408 fragment_stats,
409 relation_stats,
410 }))
411 }
412
413 async fn tiered_cache_tracing(
414 &self,
415 request: Request<TieredCacheTracingRequest>,
416 ) -> Result<Response<TieredCacheTracingResponse>, Status> {
417 let req = request.into_inner();
418
419 tracing::info!("Update tiered cache tracing config: {req:?}");
420
421 if let Some(cache) = &self.meta_cache {
422 if req.enable {
423 cache.enable_tracing();
424 } else {
425 cache.disable_tracing();
426 }
427 let mut options = TracingOptions::new();
428 if let Some(threshold) = req.record_hybrid_insert_threshold_ms {
429 options = options
430 .with_record_hybrid_insert_threshold(Duration::from_millis(threshold as _));
431 }
432 if let Some(threshold) = req.record_hybrid_get_threshold_ms {
433 options =
434 options.with_record_hybrid_get_threshold(Duration::from_millis(threshold as _));
435 }
436 if let Some(threshold) = req.record_hybrid_remove_threshold_ms {
437 options = options
438 .with_record_hybrid_remove_threshold(Duration::from_millis(threshold as _));
439 }
440 if let Some(threshold) = req.record_hybrid_fetch_threshold_ms {
441 options = options.with_record_hybrid_get_or_fetch_threshold(Duration::from_millis(
442 threshold as _,
443 ));
444 }
445 cache.update_tracing_options(options);
446 }
447
448 if let Some(cache) = &self.block_cache {
449 if req.enable {
450 cache.enable_tracing();
451 } else {
452 cache.disable_tracing();
453 }
454 let mut options = TracingOptions::new();
455 if let Some(threshold) = req.record_hybrid_insert_threshold_ms {
456 options = options
457 .with_record_hybrid_insert_threshold(Duration::from_millis(threshold as _));
458 }
459 if let Some(threshold) = req.record_hybrid_get_threshold_ms {
460 options =
461 options.with_record_hybrid_get_threshold(Duration::from_millis(threshold as _));
462 }
463 if let Some(threshold) = req.record_hybrid_remove_threshold_ms {
464 options = options
465 .with_record_hybrid_remove_threshold(Duration::from_millis(threshold as _));
466 }
467 if let Some(threshold) = req.record_hybrid_fetch_threshold_ms {
468 options = options.with_record_hybrid_get_or_fetch_threshold(Duration::from_millis(
469 threshold as _,
470 ));
471 }
472 cache.update_tracing_options(options);
473 }
474
475 Ok(Response::new(TieredCacheTracingResponse::default()))
476 }
477
478 async fn get_table_cache_refill_stats(
479 &self,
480 _request: Request<GetTableCacheRefillStatsRequest>,
481 ) -> Result<Response<GetTableCacheRefillStatsResponse>, Status> {
482 let Some(hummock_storage) = &self.hummock_storage else {
483 return Ok(Response::new(GetTableCacheRefillStatsResponse {
484 stats: "{}".to_owned(),
485 }));
486 };
487
488 let monitor_snapshot = hummock_storage
489 .table_cache_refill_monitor_snapshot()
490 .await
491 .map_err(|err| {
492 Status::internal(format!(
493 "failed to get table cache refill monitor snapshot: {e}",
494 e = err.as_report()
495 ))
496 })?;
497 let stats = TableCacheRefillStats::from(&monitor_snapshot);
498 let json_value = serde_json::to_value(stats).map_err(|err| {
499 Status::internal(format!(
500 "failed to serialize stats: {e}",
501 e = err.as_report()
502 ))
503 })?;
504 let json_string_pretty = serde_json::to_string_pretty(&json_value).map_err(|err| {
505 Status::internal(format!(
506 "failed to serialize stats: {e}",
507 e = err.as_report()
508 ))
509 })?;
510 Ok(Response::new(GetTableCacheRefillStatsResponse {
511 stats: json_string_pretty,
512 }))
513 }
514}
515
516#[derive(Debug, serde::Serialize, serde::Deserialize)]
517struct TableCacheRefillStats {
518 contexts: HashMap<u32, TableCacheRefillTableStats>,
519 streaming: HashMap<u32, Vec<u16>>,
520 serving: HashMap<u32, Vec<u16>>,
521 policies: HashMap<u32, String>,
522 default_policy: String,
523 internal: TableCacheRefillInternalStats,
524}
525
526#[derive(Debug, serde::Serialize, serde::Deserialize)]
527struct TableCacheRefillTableStats {
528 streaming: Option<Vec<u16>>,
529 serving: Option<Vec<u16>>,
530 policy: String,
531}
532
533#[derive(Debug, serde::Serialize, serde::Deserialize)]
534struct TableCacheRefillInternalStats {
535 streaming: HashMap<u32, Vec<Vec<u16>>>,
536 serving: HashMap<u32, Vec<u16>>,
537}
538
539impl From<&TableCacheRefillMonitorSnapshot> for TableCacheRefillStats {
540 fn from(snapshot: &TableCacheRefillMonitorSnapshot) -> Self {
541 let contexts = snapshot
542 .contexts
543 .iter()
544 .map(|(table_id, context)| {
545 (
546 table_id.as_raw_id(),
547 TableCacheRefillTableStats::from(context.clone()),
548 )
549 })
550 .collect::<HashMap<_, _>>();
551 let streaming = contexts
552 .iter()
553 .filter_map(|(table_id, context)| {
554 context
555 .streaming
556 .as_ref()
557 .map(|vnodes| (*table_id, vnodes.clone()))
558 })
559 .collect();
560 let serving = contexts
561 .iter()
562 .filter_map(|(table_id, context)| {
563 context
564 .serving
565 .as_ref()
566 .map(|vnodes| (*table_id, vnodes.clone()))
567 })
568 .collect();
569 let policies = snapshot
570 .policies
571 .iter()
572 .map(|(table_id, policy)| (table_id.as_raw_id(), policy.to_string()))
573 .collect();
574 let internal_streaming = snapshot
575 .streaming_table_vnode_mapping
576 .iter()
577 .map(|(table_id, bitmap)| (table_id.as_raw_id(), vec![bitmap_to_vnodes(bitmap)]))
580 .collect();
581 let internal_serving = snapshot
582 .serving_table_vnode_mapping
583 .iter()
584 .map(|(table_id, bitmap)| (table_id.as_raw_id(), bitmap_to_vnodes(bitmap)))
585 .collect();
586
587 Self {
588 contexts,
589 streaming,
590 serving,
591 policies,
592 default_policy: snapshot.default_policy.to_string(),
593 internal: TableCacheRefillInternalStats {
594 streaming: internal_streaming,
595 serving: internal_serving,
596 },
597 }
598 }
599}
600
601impl From<TableCacheRefillContext> for TableCacheRefillTableStats {
602 fn from(context: TableCacheRefillContext) -> Self {
603 Self {
604 streaming: context
605 .streaming_vnode_bitmap
606 .as_ref()
607 .map(bitmap_to_vnodes),
608 serving: context.serving_vnode_bitmap.as_ref().map(bitmap_to_vnodes),
609 policy: context.policy.to_string(),
610 }
611 }
612}
613
614fn bitmap_to_vnodes(bitmap: &Bitmap) -> Vec<u16> {
615 bitmap.iter_ones().map(|idx| idx as u16).collect()
616}
617
618pub use grpc_middleware::*;
619use risingwave_common::metrics::get_label_infallible;
620use risingwave_pb::id::FragmentId;
621
622pub mod grpc_middleware {
623 pub use risingwave_common_service::{
624 AwaitTreeMiddleware, AwaitTreeMiddlewareLayer, AwaitTreeRegistryRef, GrpcCall,
625 };
626}