rocketmq_runtime/task_group/
diagnostics.rs1use super::{TaskDetailScan, TaskDetailScope, TaskGroup, TaskGroupDiagnostics, TaskKind, TaskKindDiagnostics};
18use std::time::Duration;
19
20impl TaskGroup {
21 pub fn diagnostics_task_details(&self, scan_budget: usize, output_budget: usize) -> crate::RuntimeTaskDetails {
28 self.bounded_task_details(scan_budget, output_budget).into()
29 }
30
31 pub(crate) fn diagnostics(&self, long_running_threshold: Duration) -> TaskGroupDiagnostics {
36 let mut aggregate = TaskGroupDiagnosticsAccumulator::default();
37 let local_task_count = self.accumulate_local_diagnostics(long_running_threshold, &mut aggregate);
38 let mut pending = self.inner.registry.components_snapshot();
39 while let Some(group) = pending.pop() {
40 group.accumulate_local_diagnostics(long_running_threshold, &mut aggregate);
41 pending.extend(group.inner.registry.components_snapshot());
42 }
43 aggregate.finish(local_task_count)
44 }
45
46 pub(crate) fn bounded_task_details(&self, scan_budget: usize, output_budget: usize) -> TaskDetailScan {
55 let mut scan = TaskDetailScan::default();
56 if !self.collect_local_task_details(scan_budget, output_budget, TaskDetailScope::Local, &mut scan) {
57 return scan;
58 }
59 let mut remaining_groups = scan_budget;
60 let (mut pending, truncated) = self.inner.registry.bounded_components_snapshot(&mut remaining_groups);
61 scan.truncated |= truncated;
62 while let Some(group) = pending.pop() {
63 if scan.scanned >= scan_budget {
64 scan.truncated = true;
65 break;
66 }
67 if !group.collect_local_task_details(scan_budget, output_budget, TaskDetailScope::Subtree, &mut scan) {
68 break;
69 }
70 let (children, truncated) = group.inner.registry.bounded_components_snapshot(&mut remaining_groups);
71 scan.truncated |= truncated;
72 pending.extend(children);
73 }
74 scan.group_entries_scanned = scan_budget - remaining_groups;
75 scan
76 }
77
78 fn collect_local_task_details(
81 &self,
82 scan_budget: usize,
83 output_budget: usize,
84 scope: TaskDetailScope,
85 scan: &mut TaskDetailScan,
86 ) -> bool {
87 self.inner
88 .registry
89 .collect_task_details(scan_budget, output_budget, scope, scan)
90 }
91
92 fn accumulate_local_diagnostics(
93 &self,
94 long_running_threshold: Duration,
95 aggregate: &mut TaskGroupDiagnosticsAccumulator,
96 ) -> usize {
97 aggregate.group_count = aggregate.group_count.saturating_add(1);
98 let mut local_task_count = 0;
99 for task in self.inner.registry.tasks.iter() {
100 local_task_count += 1;
101 let elapsed = task.started_at.elapsed();
102 aggregate.record_task(task.kind, elapsed, elapsed >= long_running_threshold);
103 }
104 local_task_count
105 }
106}
107
108#[derive(Debug, Default)]
109struct TaskGroupDiagnosticsAccumulator {
110 group_count: usize,
111 task_count: usize,
112 active_by_kind: [usize; TaskKind::COUNT],
113 long_running_by_kind: [usize; TaskKind::COUNT],
114 max_elapsed_by_kind: [Duration; TaskKind::COUNT],
115}
116
117impl TaskGroupDiagnosticsAccumulator {
118 fn record_task(&mut self, kind: TaskKind, elapsed: Duration, long_running: bool) {
119 let index = kind.index();
120 self.task_count = self.task_count.saturating_add(1);
121 self.active_by_kind[index] = self.active_by_kind[index].saturating_add(1);
122 if long_running {
123 self.long_running_by_kind[index] = self.long_running_by_kind[index].saturating_add(1);
124 }
125 self.max_elapsed_by_kind[index] = self.max_elapsed_by_kind[index].max(elapsed);
126 }
127
128 fn finish(self, local_task_count: usize) -> TaskGroupDiagnostics {
129 let task_kinds = TaskKind::ALL
130 .into_iter()
131 .filter_map(|kind| {
132 let index = kind.index();
133 (self.active_by_kind[index] > 0).then_some(TaskKindDiagnostics {
134 kind,
135 active: self.active_by_kind[index],
136 long_running: self.long_running_by_kind[index],
137 max_elapsed: self.max_elapsed_by_kind[index],
138 })
139 })
140 .collect();
141
142 TaskGroupDiagnostics {
143 local_task_count,
144 group_count: self.group_count,
145 task_count: self.task_count,
146 task_kinds,
147 }
148 }
149}