-
Notifications
You must be signed in to change notification settings - Fork 0
fix: wave 0-4 gap closure — correctness, observability, CI, durability, compat #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d414e9f
6dbfb51
cc05992
e489b7d
6ca93f0
1d6bd86
daf5f49
2f427ab
3d5372a
76510b4
81cad44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -25,6 +25,7 @@ pub fn is_server_ready() -> bool { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (admin_port=0), so INFO always returns meaningful stats. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static TOTAL_COMMANDS: AtomicU64 = AtomicU64::new(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static TOTAL_CONNECTIONS: AtomicU64 = AtomicU64::new(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static CONNECTED_CLIENTS: AtomicU64 = AtomicU64::new(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Initialize the Prometheus metrics exporter and admin HTTP server. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -340,6 +341,7 @@ pub fn record_command_error(cmd: &str) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[inline] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn record_connection_opened() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TOTAL_CONNECTIONS.fetch_add(1, Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CONNECTED_CLIENTS.fetch_add(1, Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !METRICS_INITIALIZED.load(Ordering::Relaxed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -350,12 +352,19 @@ pub fn record_connection_opened() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Record a client disconnection. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[inline] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn record_connection_closed() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CONNECTED_CLIENTS.fetch_sub(1, Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !METRICS_INITIALIZED.load(Ordering::Relaxed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gauge!("moon_connected_clients").decrement(1.0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Current number of connected clients (for INFO command). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[inline] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn connected_clients() -> u64 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CONNECTED_CLIENTS.load(Ordering::Relaxed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Keyspace metrics ──────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Record keyspace hit/miss. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -464,6 +473,32 @@ pub fn update_rss_bytes(rss: u64) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gauge!("moon_rss_bytes").set(rss as f64); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Memory helpers ────────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Read process RSS from /proc/self/status (Linux only). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Returns bytes, or 0 on failure / non-Linux. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(target_os = "linux")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn get_rss_bytes() -> u64 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(status) = std::fs::read_to_string("/proc/self/status") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for line in status.lines() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(rest) = line.strip_prefix("VmRSS:") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let trimmed = rest.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(kb_str) = trimmed.strip_suffix(" kB") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(kb) = kb_str.trim().parse::<u64>() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return kb * 1024; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(not(target_os = "linux"))] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn get_rss_bytes() -> u64 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── INFO helpers ──────────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Total commands processed since server start (for INFO Stats). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -506,6 +541,54 @@ pub fn get_cpu_usage() -> (f64, f64) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (0.0, 0.0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Global replication state (for INFO) ──────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static GLOBAL_REPL_STATE: once_cell::sync::OnceCell< | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::sync::Arc<std::sync::RwLock<crate::replication::state::ReplicationState>>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > = once_cell::sync::OnceCell::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Register the global replication state for INFO queries. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn set_global_repl_state( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: std::sync::Arc<std::sync::RwLock<crate::replication::state::ReplicationState>>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+546
to
+553
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chain🏁 Script executed: # Check if the file exists and examine the code at the specified lines
if [ -f "src/admin/metrics_setup.rs" ]; then
echo "=== File exists. Checking lines 546-553 ==="
sed -n '546,553p' src/admin/metrics_setup.rs
echo ""
echo "=== Checking for std::sync::RwLock usage in this file ==="
grep -n "std::sync::RwLock\|std::sync::Mutex\|std::sync::Arc" src/admin/metrics_setup.rs | head -20
else
echo "File not found at src/admin/metrics_setup.rs"
fiRepository: pilotspace/moon Length of output: 865 🏁 Script executed: # Get context around lines 546-553 to understand the usage better
if [ -f "src/admin/metrics_setup.rs" ]; then
echo "=== Context around lines 546-553 ==="
sed -n '540,560p' src/admin/metrics_setup.rs
fiRepository: pilotspace/moon Length of output: 925 🏁 Script executed: # Check if parking_lot is available in the project dependencies
echo "=== Checking Cargo.toml for parking_lot dependency ==="
if grep -r "parking_lot" Cargo.toml 2>/dev/null; then
echo "parking_lot found in main Cargo.toml"
else
echo "parking_lot may be in workspace member Cargo.toml or feature-gated"
find . -name "Cargo.toml" -type f -exec grep -l "parking_lot" {} \; 2>/dev/null | head -10
fiRepository: pilotspace/moon Length of output: 171 Replace Both the static type (line 547) and the function parameter (line 552) use Code snippet🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let _ = GLOBAL_REPL_STATE.set(state); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Get replication info for INFO command: (role, connected_slaves, master_repl_offset, repl_id). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Also updates the Prometheus replication lag gauge as a side-effect. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn get_replication_info() -> (&'static str, usize, u64, String) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+546
to
+559
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3. std::sync::rwlock in metrics src/admin/metrics_setup.rs introduces std::sync::RwLock for global replication state, contrary to the project locking rule requiring parking_lot primitives. This increases risk of poisoning behavior and violates the lock primitive standardization requirement. Agent Prompt
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(state) = GLOBAL_REPL_STATE.get() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(guard) = state.read() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let role = match &guard.role { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| crate::replication::state::ReplicationRole::Master => "master", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| crate::replication::state::ReplicationRole::Replica { .. } => "slave", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let slaves = guard.replicas.len(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let offset = guard.master_repl_offset.load(Ordering::Relaxed); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let repl_id = guard.repl_id.clone(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Update Prometheus lag gauge: max lag across all replicas. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !guard.replicas.is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let max_lag_bytes = guard | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replicas | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(|r| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let ack: u64 = r | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ack_offsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(|a| a.load(Ordering::Relaxed)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .sum(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| offset.saturating_sub(ack) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .max() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .unwrap_or(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| record_replication_lag(max_lag_bytes, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+569
to
+585
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reset replication lag to zero when no replicas are connected. The gauge is only updated inside the non-empty branch, so after the last replica disconnects Suggested fix- if !guard.replicas.is_empty() {
- let max_lag_bytes = guard
- .replicas
- .iter()
- .map(|r| {
- let ack: u64 = r
- .ack_offsets
- .iter()
- .map(|a| a.load(Ordering::Relaxed))
- .sum();
- offset.saturating_sub(ack)
- })
- .max()
- .unwrap_or(0);
- record_replication_lag(max_lag_bytes, 0);
- }
+ let max_lag_bytes = guard
+ .replicas
+ .iter()
+ .map(|r| {
+ let ack: u64 = r
+ .ack_offsets
+ .iter()
+ .map(|a| a.load(Ordering::Relaxed))
+ .sum();
+ offset.saturating_sub(ack)
+ })
+ .max()
+ .unwrap_or(0);
+ record_replication_lag(max_lag_bytes, 0);
return (role, slaves, offset, repl_id);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (role, slaves, offset, repl_id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ("master", 0, 0, "0".repeat(40)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Global SLOWLOG ───────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Global slowlog instance accessible from any handler thread. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,21 +130,54 @@ pub fn readyz() -> Frame { | |
| } | ||
| } | ||
|
|
||
| /// Format bytes as human-readable (e.g. "1.23M", "456.78K"). | ||
| fn format_memory_human(bytes: u64) -> String { | ||
| const KB: f64 = 1024.0; | ||
| const MB: f64 = 1024.0 * 1024.0; | ||
| const GB: f64 = 1024.0 * 1024.0 * 1024.0; | ||
| let b = bytes as f64; | ||
| if b >= GB { | ||
| format!("{:.2}G", b / GB) | ||
| } else if b >= MB { | ||
| format!("{:.2}M", b / MB) | ||
| } else if b >= KB { | ||
| format!("{:.2}K", b / KB) | ||
| } else { | ||
| format!("{bytes}B") | ||
| } | ||
|
Comment on lines
+133
to
+147
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. format! in format_memory_human src/command/connection.rs introduces format!-based string building in the command path, which performs heap allocations and formatting work in a hot module. This violates the no-allocation/formatting requirement for code under src/command/. Agent Prompt
|
||
| } | ||
|
|
||
| /// INFO command handler. | ||
| /// | ||
| /// Returns a BulkString with minimal INFO sections. | ||
| /// Returns a BulkString with server info sections matching Redis INFO format. | ||
| pub fn info(db: &Database, _args: &[Frame]) -> Frame { | ||
| let mut sections = String::new(); | ||
| use std::fmt::Write as _; | ||
| let mut sections = String::with_capacity(2048); | ||
|
|
||
| sections.push_str("# Server\r\n"); | ||
| sections.push_str("redis_version:0.1.0\r\n"); | ||
| sections.push_str("moon:true\r\n"); | ||
| sections.push_str("\r\n"); | ||
|
|
||
| sections.push_str("# Clients\r\n"); | ||
| let _ = write!( | ||
| sections, | ||
| "connected_clients:{}\r\n", | ||
| crate::admin::metrics_setup::connected_clients(), | ||
| ); | ||
| sections.push_str("\r\n"); | ||
|
|
||
| sections.push_str("# Memory\r\n"); | ||
| let rss = crate::admin::metrics_setup::get_rss_bytes(); | ||
| let _ = write!( | ||
| sections, | ||
| "used_memory:{rss}\r\n\ | ||
| used_memory_human:{human}\r\n\ | ||
| used_memory_rss:{rss}\r\n\ | ||
| used_memory_peak:{rss}\r\n", | ||
|
Comment on lines
+174
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 5. Info keys prefixed by spaces INFO uses multi-line string literals with \ line continuation plus indentation, which embeds leading spaces into field names (e.g. " used_memory_human"), breaking Redis INFO compatibility and parsers. Agent Prompt
Comment on lines
+171
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This now reports the current RSS as the peak, so the “peak” value will shrink again after memory is released. INFO consumers usually assume 🤖 Prompt for AI Agents |
||
| rss = rss, | ||
| human = format_memory_human(rss), | ||
| ); | ||
| sections.push_str("\r\n"); | ||
|
|
||
| sections.push_str("# Persistence\r\n"); | ||
|
|
@@ -196,7 +229,6 @@ pub fn info(db: &Database, _args: &[Frame]) -> Frame { | |
| sections.push_str("\r\n"); | ||
|
|
||
| sections.push_str("# MoonStore\r\n"); | ||
| use std::fmt::Write as _; | ||
| let _ = write!( | ||
| sections, | ||
| "disk_offload_enabled:{}\r\n", | ||
|
|
@@ -228,13 +260,15 @@ pub fn info(db: &Database, _args: &[Frame]) -> Frame { | |
| sections.push_str("\r\n"); | ||
|
|
||
| // # Replication | ||
| // NOTE: placeholder — always reports master with 0 replicas. | ||
| // TODO: wire to actual ReplicationState when replication is implemented. | ||
| sections.push_str("# Replication\r\n"); | ||
| sections.push_str("role:master\r\n"); | ||
| sections.push_str("connected_slaves:0\r\n"); | ||
| sections.push_str("master_replid:0000000000000000000000000000000000000000\r\n"); | ||
| sections.push_str("master_repl_offset:0\r\n"); | ||
| let (role, slaves, offset, repl_id) = crate::admin::metrics_setup::get_replication_info(); | ||
| let _ = write!( | ||
| sections, | ||
| "role:{role}\r\n\ | ||
| connected_slaves:{slaves}\r\n\ | ||
| master_replid:{repl_id}\r\n\ | ||
| master_repl_offset:{offset}\r\n", | ||
| ); | ||
| sections.push_str("\r\n"); | ||
|
|
||
| sections.push_str("# Keyspace\r\n"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard the connected-client count against unmatched closes.
Line 355 unconditionally decrements both the atomic state and the Prometheus gauge. If a close path fires twice or after a partially-opened connection,
CONNECTED_CLIENTSwraps tou64::MAXand the gauge can go negative.Suggested fix
pub fn record_connection_closed() { - CONNECTED_CLIENTS.fetch_sub(1, Ordering::Relaxed); + let prev = CONNECTED_CLIENTS + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |n| { + Some(n.saturating_sub(1)) + }) + .unwrap_or(0); + if prev == 0 { + return; + } if !METRICS_INITIALIZED.load(Ordering::Relaxed) { return; } gauge!("moon_connected_clients").decrement(1.0); }📝 Committable suggestion
🤖 Prompt for AI Agents