veloren_server/sys/
metrics.rs1use crate::{
2 HwStats, Tick, TickStart,
3 chunk_generator::ChunkGenerator,
4 metrics::{EcsSystemMetrics, JobMetrics, PhysicsMetrics, QueryServerMetrics, TickMetrics},
5};
6use common::{resources::TimeOfDay, slowjob::SlowJobPool, terrain::TerrainGrid};
7use common_ecs::{Job, Origin, Phase, SysMetrics, System};
8use specs::{Entities, Join, Read, ReadExpect};
9use std::{
10 sync::{Arc, Mutex},
11 time::Instant,
12};
13use veloren_query_server::server::Metrics as RawQueryServerMetrics;
14
15#[derive(Default)]
17pub struct Sys;
18impl<'a> System<'a> for Sys {
19 type SystemData = (
20 Entities<'a>,
21 ReadExpect<'a, HwStats>,
22 ReadExpect<'a, Tick>,
23 ReadExpect<'a, TimeOfDay>,
24 ReadExpect<'a, TickStart>,
25 ReadExpect<'a, ChunkGenerator>,
26 Option<Read<'a, TerrainGrid>>,
27 Read<'a, SysMetrics>,
28 Read<'a, common_ecs::PhysicsMetrics>,
29 ReadExpect<'a, SlowJobPool>,
30 ReadExpect<'a, EcsSystemMetrics>,
31 ReadExpect<'a, TickMetrics>,
32 ReadExpect<'a, PhysicsMetrics>,
33 ReadExpect<'a, JobMetrics>,
34 Option<Read<'a, Arc<Mutex<RawQueryServerMetrics>>>>,
35 ReadExpect<'a, QueryServerMetrics>,
36 );
37
38 const NAME: &'static str = "metrics";
39 const ORIGIN: Origin = Origin::Server;
40 const PHASE: Phase = Phase::Apply;
41
42 fn run(
43 _job: &mut Job<Self>,
44 (
45 entities,
46 hw_stats,
47 tick,
48 time_of_day,
49 tick_start,
50 chunk_generator,
51 terrain,
52 sys_metrics,
53 phys_metrics,
54 slowjobpool,
55 export_ecs,
56 export_tick,
57 export_physics,
58 export_jobs,
59 raw_query_server,
60 export_query_server,
61 ): Self::SystemData,
62 ) {
63 const NANOSEC_PER_SEC: f64 = std::time::Duration::from_secs(1).as_nanos() as f64;
64
65 let start = Instant::now();
66
67 let mut state = sys_metrics.stats.lock().unwrap();
68 state.remove(Self::NAME);
70
71 for (name, stat) in common_ecs::gen_stats(
72 &state,
73 tick_start.0,
74 hw_stats.rayon_threads,
75 hw_stats.hardware_threads,
76 ) {
77 export_ecs
78 .system_start_time
79 .with_label_values(&[&name])
80 .set(stat.start_ns() as i64);
81 export_ecs
82 .system_thread_avg
83 .with_label_values(&[&name])
84 .set(stat.avg_threads() as f64);
85 let len = stat.length_ns();
86 export_ecs
87 .system_length_time
88 .with_label_values(&[&name])
89 .set(len as i64);
90 export_ecs
91 .system_length_count
92 .with_label_values(&[&name])
93 .inc_by(len);
94 export_ecs
95 .system_length_hist
96 .with_label_values(&[&name])
97 .observe(len as f64 / NANOSEC_PER_SEC);
98 }
99
100 export_tick.time_of_day.set(time_of_day.0);
102 if tick.0.rem_euclid(100) == 0 {
103 if let Some(terrain) = terrain.as_ref() {
104 let mut chonk_cnt = 0;
105 let mut group_cnt = 0;
106 let chunk_cnt = terrain.iter().fold(0, |a, (_, c)| {
107 chonk_cnt += 1;
108 group_cnt += c.sub_chunk_groups();
109 a + c.sub_chunks_len()
110 });
111 export_tick.chonks_count.set(chonk_cnt as i64);
112 export_tick.chunks_count.set(chunk_cnt as i64);
113 export_tick.chunk_groups_count.set(group_cnt as i64);
114 }
115
116 let entity_count = entities.join().count();
117 export_tick.entity_count.set(entity_count as i64);
118 }
119 common_base::plot!("entity count", entities.join().count() as f64);
120 common_base::plot!(
121 "pending chunks",
122 chunk_generator.pending_chunks().count() as f64
123 );
124 if let Some(terrain) = terrain.as_ref() {
125 common_base::plot!("chunk count", terrain.iter().count() as f64);
126 }
127
128 export_physics
130 .entity_entity_collision_checks_count
131 .inc_by(phys_metrics.entity_entity_collision_checks);
132 export_physics
133 .entity_entity_collisions_count
134 .inc_by(phys_metrics.entity_entity_collisions);
135
136 for (name, jobs) in slowjobpool.take_metrics() {
138 let queried = export_jobs.job_queried_hst.with_label_values(&[&name]);
139 let executed = export_jobs.job_execution_hst.with_label_values(&[&name]);
140 for job in jobs {
141 queried.observe(
142 job.execution_start
143 .duration_since(job.queue_created)
144 .as_secs_f64(),
145 );
146 executed.observe(
147 job.execution_end
148 .duration_since(job.execution_start)
149 .as_secs_f64(),
150 );
151 }
152 }
153
154 export_ecs
156 .system_start_time
157 .with_label_values(&["metrics"])
158 .set(start.duration_since(tick_start.0).as_nanos() as i64);
159 export_ecs
160 .system_thread_avg
161 .with_label_values(&["metrics"])
162 .set(1.0);
163 let len = start.elapsed().as_nanos() as u64;
164 export_ecs
165 .system_length_time
166 .with_label_values(&["metrics"])
167 .set(len as i64);
168 export_ecs
169 .system_length_count
170 .with_label_values(&["metrics"])
171 .inc_by(len);
172 export_ecs
173 .system_length_hist
174 .with_label_values(&["metrics"])
175 .observe(len as f64 / NANOSEC_PER_SEC);
176
177 if let Some(Ok(metrics)) = raw_query_server
178 .as_ref()
179 .map(|m| m.lock().map(|mut metrics| metrics.reset()))
181 {
182 export_query_server.apply(metrics);
183 }
184 }
185}