1use crate::{lock, Core, OwnerPhase, RegistrationStatus};
4use nadisc::managed::DiscoveryError;
5use std::sync::{atomic::Ordering, Weak};
6use tokio::time::Instant;
7
8#[derive(Clone)]
10pub struct RuntimeObserver {
11 pub(crate) core: Weak<Core>,
12}
13
14#[derive(Clone, Debug)]
16pub struct ConnectionStatus {
17 pub name: String,
19 pub prepared: bool,
21 pub preparation_error: Option<DiscoveryError>,
23 pub observations: usize,
25 pub usable_observations: usize,
27 pub oldest_observation_age_ms: Option<u64>,
29 pub queries: u64,
31 pub query_errors: u64,
33 pub query_micros: u64,
35}
36
37#[derive(Clone, Debug)]
39pub struct RuntimeStatus {
40 pub phase: OwnerPhase,
42 pub supervisor_failed: bool,
44 pub default_connection: Option<String>,
46 pub connections: Vec<ConnectionStatus>,
48 pub registrations: Vec<RegistrationStatus>,
50 pub retained_instances: usize,
52 pub snapshot_bytes: usize,
54 pub transport_tasks: usize,
56 pub shutdown_remaining_ms: Option<u64>,
58}
59
60impl RuntimeObserver {
61 pub fn snapshot(&self) -> Option<RuntimeStatus> {
67 let core = self.core.upgrade()?;
68 let gate = lock(&core.gate);
69 let now = Instant::now();
70 let connections = core
71 .connections
72 .values()
73 .map(|connection| {
74 let slots = lock(&connection.slots);
75 let mut usable = 0;
76 let mut oldest = None;
77 for slot in slots.values() {
78 let state = lock(&slot.state);
79 if !gate.failed
80 && !gate.close_started
81 && crate::observation::check(connection, &state, false).is_ok()
82 {
83 usable += 1;
84 }
85 if let Some(observed) = state.observed_at {
86 let age = now
87 .saturating_duration_since(observed)
88 .as_millis()
89 .min(u64::MAX as u128) as u64;
90 oldest = Some(oldest.map_or(age, |old: u64| old.max(age)));
91 }
92 }
93 ConnectionStatus {
94 name: connection.name.clone(),
95 prepared: *connection.prepared.borrow(),
96 preparation_error: lock(&connection.error).clone(),
97 observations: slots.len(),
98 usable_observations: usable,
99 oldest_observation_age_ms: oldest,
100 queries: connection.queries.load(Ordering::Relaxed),
101 query_errors: connection.query_errors.load(Ordering::Relaxed),
102 query_micros: connection.query_micros.load(Ordering::Relaxed),
103 }
104 })
105 .collect();
106 let registrations = gate
107 .registrations
108 .values()
109 .map(|record| record.current_status(&gate))
110 .collect();
111 let quota = lock(&core.quota);
112 Some(RuntimeStatus {
113 phase: gate.phase,
114 supervisor_failed: gate.failed,
115 default_connection: core.default_connection.clone(),
116 connections,
117 registrations,
118 retained_instances: quota.instances,
119 snapshot_bytes: quota.bytes,
120 transport_tasks: core.limits.max_transport_tasks - core.transports.available_permits(),
121 shutdown_remaining_ms: gate.shutdown_deadline.map(|deadline| {
122 deadline
123 .saturating_duration_since(now)
124 .as_millis()
125 .min(u64::MAX as u128) as u64
126 }),
127 })
128 }
129}