Skip to main content

nadisc_runtime/
status.rs

1//! 只读诊断按已冻结配置连接聚合,不把任意服务输入或实例地址变为指标标签。
2
3use crate::{lock, Core, OwnerPhase, RegistrationStatus};
4use nadisc::managed::DiscoveryError;
5use std::sync::{atomic::Ordering, Weak};
6use tokio::time::Instant;
7
8/// 不拥有关闭权的运行状态观察入口,不因采集状态触发网络访问。
9#[derive(Clone)]
10pub struct RuntimeObserver {
11    pub(crate) core: Weak<Core>,
12}
13
14/// 单个 qualifier 的准备状态、观察容量和累计查询统计。
15#[derive(Clone, Debug)]
16pub struct ConnectionStatus {
17    /// 连接的本地 qualifier,不是后端服务名称。
18    pub name: String,
19    /// 连接是否已达到所需作用域的访问条件。
20    pub prepared: bool,
21    /// 最近一次连接准备错误,成功恢复后可清除。
22    pub preparation_error: Option<DiscoveryError>,
23    /// 该连接当前持有的服务观察数量。
24    pub observations: usize,
25    /// 按当前身份和时效规则仍可用于路由的观察数量。
26    pub usable_observations: usize,
27    /// 保留证据中的最大年龄,单位为毫秒;没有证据时为空。
28    pub oldest_observation_age_ms: Option<u64>,
29    /// 该连接累计发起的查询次数。
30    pub queries: u64,
31    /// 该连接累计查询错误次数。
32    pub query_errors: u64,
33    /// 该连接查询累计耗时,单位为微秒。
34    pub query_micros: u64,
35}
36
37/// 同一 owner 下的连接、注册、资源使用和关闭进度摘要。
38#[derive(Clone, Debug)]
39pub struct RuntimeStatus {
40    /// owner 当前生命周期阶段。
41    pub phase: OwnerPhase,
42    /// 后台监督是否异常退出;异常时停止使用现有路由证据。
43    pub supervisor_failed: bool,
44    /// 已解析的默认连接 qualifier;未配置 primary 且无显式覆盖时为空。
45    pub default_connection: Option<String>,
46    /// 以 qualifier 为键的连接目录,不按顺序推断默认连接。
47    pub connections: Vec<ConnectionStatus>,
48    /// 注册目标集合,包含各目标独立的状态或发布策略。
49    pub registrations: Vec<RegistrationStatus>,
50    /// 所有保留快照累计占用的实例数量。
51    pub retained_instances: usize,
52    /// 所有保留快照占用的估算字节数。
53    pub snapshot_bytes: usize,
54    /// 当前占用容量许可的传输任务数量。
55    pub transport_tasks: usize,
56    /// 冻结的关闭总期限剩余毫秒数;尚未关闭时为空。
57    pub shutdown_remaining_ms: Option<u64>,
58}
59
60impl RuntimeObserver {
61    /// 业务作用:同步读取容量、注册和连接证据摘要,不在探针或指标抓取时发起网络查询。
62    ///
63    /// 参数说明:无。
64    ///
65    /// 返回:owner 尚有状态时给出按当前身份代次裁决的有限摘要;所有资源释放后为空,不展示凭据、URL 或实例 metadata。
66    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}