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> {
65 let core = self.core.upgrade()?;
66 let gate = lock(&core.gate);
67 let now = Instant::now();
68 let connections = core
69 .connections
70 .values()
71 .map(|connection| {
72 let slots = lock(&connection.slots);
73 let mut usable = 0;
74 let mut oldest = None;
75 for slot in slots.values() {
76 let state = lock(&slot.state);
77 if !gate.failed
78 && !gate.close_started
79 && crate::observation::check(connection, &state, false).is_ok()
80 {
81 usable += 1;
82 }
83 if let Some(observed) = state.observed_at {
84 let age = now
85 .saturating_duration_since(observed)
86 .as_millis()
87 .min(u64::MAX as u128) as u64;
88 oldest = Some(oldest.map_or(age, |old: u64| old.max(age)));
89 }
90 }
91 ConnectionStatus {
92 name: connection.name.clone(),
93 prepared: *connection.prepared.borrow(),
94 preparation_error: lock(&connection.error).clone(),
95 observations: slots.len(),
96 usable_observations: usable,
97 oldest_observation_age_ms: oldest,
98 queries: connection.queries.load(Ordering::Relaxed),
99 query_errors: connection.query_errors.load(Ordering::Relaxed),
100 query_micros: connection.query_micros.load(Ordering::Relaxed),
101 }
102 })
103 .collect();
104 let registrations = gate
105 .registrations
106 .values()
107 .map(|record| record.current_status(&gate))
108 .collect();
109 let quota = lock(&core.quota);
110 Some(RuntimeStatus {
111 phase: gate.phase,
112 supervisor_failed: gate.failed,
113 default_connection: core.default_connection.clone(),
114 connections,
115 registrations,
116 retained_instances: quota.instances,
117 snapshot_bytes: quota.bytes,
118 transport_tasks: core.limits.max_transport_tasks - core.transports.available_permits(),
119 shutdown_remaining_ms: gate.shutdown_deadline.map(|deadline| {
120 deadline
121 .saturating_duration_since(now)
122 .as_millis()
123 .min(u64::MAX as u128) as u64
124 }),
125 })
126 }
127}