tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

commit fe73aded5038a94f48fb5a11c03c0b955ce15fce
parent 78e63ecd7dd46ba35c268626ef0d50bfb4759443
Author: triesap <tyson@radroots.org>
Date:   Tue, 11 Aug 2026 03:20:52 +0000

runtime: expose live resource saturation metrics

Diffstat:
Mcrates/tangle_runtime/src/ops.rs | 2++
Mcrates/tangle_runtime/src/runtime.rs | 44+++++++++++++++++++++++++++++++++++++++++---
Mcrates/tangle_runtime/src/session.rs | 9++++++---
3 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/crates/tangle_runtime/src/ops.rs b/crates/tangle_runtime/src/ops.rs @@ -342,6 +342,7 @@ mod tests { "tangle_req_messages_total", "tangle_runtime_uptime_seconds", "tangle_stored_event_offsets_total", + "tangle_subscriptions_current", "tangle_subscriptions_closed_total", "tangle_subscriptions_opened_total", "tangle_ws_connections_current", @@ -356,6 +357,7 @@ mod tests { assert_eq!(metrics_value["tangle_ws_connections_total"], 1); assert_eq!(metrics_value["tangle_client_messages_total"], 1); assert_eq!(metrics_value["tangle_req_messages_total"], 1); + assert_eq!(metrics_value["tangle_subscriptions_current"], 1); assert_eq!(metrics_value["tangle_subscriptions_opened_total"], 1); assert_eq!(metrics_value["tangle_auth_success_total"], 1); assert_eq!(metrics_value["tangle_auth_failure_total"], 1); diff --git a/crates/tangle_runtime/src/runtime.rs b/crates/tangle_runtime/src/runtime.rs @@ -93,6 +93,10 @@ pub trait RelayRuntimeHooks: Send + Sync { fn event_stored(&self, _context: &RelayEventStoredContext) {} + fn storage_used_bytes(&self) -> Option<u64> { + None + } + fn plan_query(&self, _context: &RelayQueryProjectionContext) -> RelayProjectionQueryPlan { RelayProjectionQueryPlan::default() } @@ -1749,9 +1753,11 @@ impl RelayRuntimeHandle { rate_limit_context.connection_id(), now.as_u64(), ); - if let EventAdmissionDecision::Reject { message } = - self.inner.hooks.admit_event(&admission) - { + let admission_decision = self.inner.hooks.admit_event(&admission); + if let Some(used_bytes) = self.inner.hooks.storage_used_bytes() { + self.inner.metrics.record_disk_used_bytes(used_bytes); + } + if let EventAdmissionDecision::Reject { message } = admission_decision { let message = RelayMessage::Ok { event_id, accepted: false, @@ -2428,6 +2434,7 @@ struct TangleRuntimeMetricsInner { count_messages: AtomicU64, auth_messages: AtomicU64, close_messages: AtomicU64, + active_subscriptions: AtomicUsize, opened_subscriptions: AtomicU64, closed_subscriptions: AtomicU64, stored_event_offsets: AtomicU64, @@ -2479,6 +2486,7 @@ pub struct TangleRuntimeMetricsSnapshot { tangle_count_messages_total: u64, tangle_auth_messages_total: u64, tangle_close_messages_total: u64, + tangle_subscriptions_current: usize, tangle_subscriptions_opened_total: u64, tangle_subscriptions_closed_total: u64, tangle_stored_event_offsets_total: u64, @@ -2545,6 +2553,10 @@ impl TangleRuntimeMetricsSnapshot { self.tangle_subscriptions_opened_total } + pub fn active_subscriptions(&self) -> usize { + self.tangle_subscriptions_current + } + pub fn closed_subscriptions(&self) -> u64 { self.tangle_subscriptions_closed_total } @@ -2571,6 +2583,7 @@ impl TangleRuntimeMetrics { count_messages: AtomicU64::new(0), auth_messages: AtomicU64::new(0), close_messages: AtomicU64::new(0), + active_subscriptions: AtomicUsize::new(0), opened_subscriptions: AtomicU64::new(0), closed_subscriptions: AtomicU64::new(0), stored_event_offsets: AtomicU64::new(0), @@ -2618,6 +2631,7 @@ impl TangleRuntimeMetrics { tangle_count_messages_total: self.count_messages(), tangle_auth_messages_total: self.auth_messages(), tangle_close_messages_total: self.close_messages(), + tangle_subscriptions_current: self.active_subscriptions(), tangle_subscriptions_opened_total: self.opened_subscriptions(), tangle_subscriptions_closed_total: self.closed_subscriptions(), tangle_stored_event_offsets_total: self.stored_event_offsets(), @@ -2689,6 +2703,10 @@ impl TangleRuntimeMetrics { self.inner.opened_subscriptions.load(Ordering::Relaxed) } + pub fn active_subscriptions(&self) -> usize { + self.inner.active_subscriptions.load(Ordering::Relaxed) + } + pub fn closed_subscriptions(&self) -> u64 { self.inner.closed_subscriptions.load(Ordering::Relaxed) } @@ -2855,12 +2873,28 @@ impl TangleRuntimeMetrics { pub fn record_subscription_opened(&self) -> u64 { self.inner + .active_subscriptions + .fetch_add(1, Ordering::Relaxed); + self.inner .opened_subscriptions .fetch_add(1, Ordering::Relaxed) + 1 } pub fn record_subscriptions_closed(&self, count: usize) -> u64 { + let mut current = self.active_subscriptions(); + loop { + let remaining = current.saturating_sub(count); + match self.inner.active_subscriptions.compare_exchange( + current, + remaining, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => break, + Err(actual) => current = actual, + } + } self.inner.closed_subscriptions.fetch_add( u64::try_from(count).expect("subscription count fits in u64"), Ordering::Relaxed, @@ -3150,8 +3184,10 @@ mod tests { assert_eq!(runtime.metrics().client_messages(), 1); assert_eq!(runtime.metrics().req_messages(), 1); assert_eq!(runtime.metrics().record_subscription_opened(), 1); + assert_eq!(runtime.metrics().active_subscriptions(), 1); assert_eq!(runtime.metrics().opened_subscriptions(), 1); assert_eq!(runtime.metrics().record_subscriptions_closed(1), 1); + assert_eq!(runtime.metrics().active_subscriptions(), 0); assert_eq!(runtime.metrics().closed_subscriptions(), 1); assert_eq!(runtime.metrics().record_stored_event_offset(), 1); assert_eq!(runtime.metrics().stored_event_offsets(), 1); @@ -3182,6 +3218,7 @@ mod tests { assert_eq!(snapshot.total_sessions(), 1); assert_eq!(snapshot.client_messages(), 1); assert_eq!(snapshot.req_messages(), 1); + assert_eq!(snapshot.active_subscriptions(), 0); assert_eq!(snapshot.opened_subscriptions(), 1); assert_eq!(snapshot.closed_subscriptions(), 1); assert_eq!(snapshot.stored_event_offsets(), 1); @@ -3240,6 +3277,7 @@ mod tests { assert_eq!(value["tangle_readiness_ready"], true); assert_eq!(value["tangle_ws_connections_current"], 1); + assert_eq!(value["tangle_subscriptions_current"], 0); assert_eq!(value["tangle_auth_success_total"], 1); assert_eq!(value["tangle_event_admitted_total"], 1); assert_eq!(value["tangle_event_bus_published_offsets_total"], 1); diff --git a/crates/tangle_runtime/src/session.rs b/crates/tangle_runtime/src/session.rs @@ -562,8 +562,10 @@ impl TangleWebSocketSession { self.subscription_permits .insert(subscription_id.clone(), permit); } - metrics.record_subscription_opened(); - logging::log_subscription_opened(self.connection_id, &subscription_id); + if !already_subscribed { + metrics.record_subscription_opened(); + logging::log_subscription_opened(self.connection_id, &subscription_id); + } } Ok(replies) } @@ -1154,7 +1156,8 @@ mod tests { assert_eq!(snapshot.client_messages(), 5); assert_eq!(snapshot.req_messages(), 3); assert_eq!(snapshot.close_messages(), 2); - assert_eq!(snapshot.opened_subscriptions(), 3); + assert_eq!(snapshot.active_subscriptions(), 0); + assert_eq!(snapshot.opened_subscriptions(), 2); assert_eq!(snapshot.closed_subscriptions(), 2); let _ = std::fs::remove_dir_all(root);