myko/server/
entity_set_stats.rs1use std::{
16 sync::{
17 OnceLock,
18 atomic::{AtomicU64, Ordering},
19 },
20 thread,
21 time::Duration,
22};
23
24use dashmap::DashMap;
25
26const WINDOW_MS: u64 = 1000;
27
28#[derive(Default)]
30struct Counts {
31 set: AtomicU64,
32 del: AtomicU64,
33}
34
35fn counts() -> &'static DashMap<String, Counts> {
36 static C: OnceLock<DashMap<String, Counts>> = OnceLock::new();
37 C.get_or_init(DashMap::new)
38}
39
40#[inline]
43pub fn record_set(entity_type: &str) {
44 counts()
45 .entry(entity_type.to_string())
46 .or_default()
47 .set
48 .fetch_add(1, Ordering::Relaxed);
49}
50
51#[inline]
54pub fn record_del(entity_type: &str) {
55 counts()
56 .entry(entity_type.to_string())
57 .or_default()
58 .del
59 .fetch_add(1, Ordering::Relaxed);
60}
61
62pub fn start_periodic_logger() {
64 static STARTED: OnceLock<()> = OnceLock::new();
65 if STARTED.set(()).is_err() {
66 return;
67 }
68 let _ = thread::Builder::new()
69 .name("myko-entity-set-stats".to_string())
70 .spawn(run_loop)
71 .map_err(|e| {
72 tracing::warn!(
73 target: "myko::server::entity_set_stats",
74 "Failed to spawn entity_set_stats thread: {}", e
75 )
76 });
77}
78
79fn run_loop() {
80 loop {
81 thread::sleep(Duration::from_millis(WINDOW_MS));
82 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(emit_window));
83 }
84}
85
86fn emit_window() {
87 let mut snap: Vec<(String, u64, u64)> = counts()
89 .iter()
90 .filter_map(|e| {
91 let sets = e.value().set.swap(0, Ordering::Relaxed);
92 let dels = e.value().del.swap(0, Ordering::Relaxed);
93 if sets == 0 && dels == 0 {
94 None
95 } else {
96 Some((e.key().clone(), sets, dels))
97 }
98 })
99 .collect();
100 if snap.is_empty() {
101 return;
102 }
103 snap.sort_by_key(|b| std::cmp::Reverse(b.1 + b.2));
105 let set_total: u64 = snap.iter().map(|s| s.1).sum();
106 let del_total: u64 = snap.iter().map(|s| s.2).sum();
107 let detail = snap
108 .iter()
109 .map(|(et, s, d)| format!("{}=set:{} del:{}", et, s, d))
110 .collect::<Vec<_>>()
111 .join(", ");
112 tracing::debug!(
113 target: "myko::server::entity_set_stats",
114 "[entity] window={}ms set_total={} del_total={} [{}]",
115 WINDOW_MS,
116 set_total,
117 del_total,
118 detail,
119 );
120}