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    /// 返回:owner 尚有状态时给出按当前身份代次裁决的有限摘要;所有资源释放后为空,不展示凭据、URL 或实例 metadata。
64    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}