Skip to main content

nadisc_runtime/
config.rs

1//! 与配置加载器和宿主框架无关的容量、时效与注册策略。
2
3use nadisc::managed::{DiscoveryError, ErrorCode, Result};
4use serde::{Deserialize, Serialize};
5
6/// owner 共享的资源上限;超出容量时拒绝新增工作。
7#[derive(Clone, Debug, Deserialize, Serialize)]
8#[serde(default, deny_unknown_fields)]
9pub struct Limits {
10    /// 同时保留的连接数量上限。
11    pub max_connections: usize,
12    /// owner 可持有的注册目标数量上限。
13    pub max_registrations: usize,
14    /// 每个连接可同时观察的服务数量上限。
15    pub max_service_watches_per_connection: usize,
16    /// owner 后端操作的并发上限。
17    pub max_parallel_operations: usize,
18    /// owner 同时持有的后端 watch 流数量上限。
19    pub max_watch_streams: usize,
20    /// 单个服务快照允许包含的实例数量上限。
21    pub max_instances_per_service: usize,
22    /// owner 所有保留快照的实例总数上限。
23    pub max_total_instances: usize,
24    /// owner 所有保留快照的估算字节总数上限。
25    pub max_snapshot_bytes: usize,
26    /// 连接驱动与正文等传输任务的共享容量上限。
27    pub max_transport_tasks: usize,
28}
29
30impl Default for Limits {
31    /// 业务作用:为连接、后台工作与被持有的快照设置有限容量。
32    /// 参数说明:无。
33    /// 返回:所有限制均为正值的默认容量。
34    fn default() -> Self {
35        Self {
36            max_connections: 16,
37            max_registrations: 64,
38            max_service_watches_per_connection: 1024,
39            max_parallel_operations: 4,
40            max_watch_streams: 64,
41            max_instances_per_service: 4096,
42            max_total_instances: 65536,
43            max_snapshot_bytes: 64 * 1024 * 1024,
44            max_transport_tasks: 4096,
45        }
46    }
47}
48
49impl Limits {
50    /// 业务作用:拒绝无法执行或可能溢出的容量设置。
51    /// 参数说明:无。
52    /// 返回:合法容量可构造资源池;零容量和异常大容量返回配置错误。
53    pub fn validate(&self) -> Result<()> {
54        for value in [
55            self.max_connections,
56            self.max_registrations,
57            self.max_service_watches_per_connection,
58            self.max_parallel_operations,
59            self.max_watch_streams,
60            self.max_instances_per_service,
61            self.max_total_instances,
62            self.max_snapshot_bytes,
63            self.max_transport_tasks,
64        ] {
65            if value == 0 || value > u32::MAX as usize {
66                return Err(invalid("capacity is outside the supported range"));
67            }
68        }
69        if self.max_instances_per_service > self.max_total_instances {
70            return Err(invalid("per-service capacity exceeds total capacity"));
71        }
72        if self.max_connections > 256
73            || self.max_registrations > 65536
74            || self.max_service_watches_per_connection > 65536
75            || self.max_parallel_operations > 4096
76            || self.max_transport_tasks > 65536
77            || self.max_watch_streams > 4096
78            || self
79                .max_connections
80                .saturating_mul(self.max_service_watches_per_connection)
81                > 65536
82        {
83            return Err(invalid(
84                "task or connection capacity exceeds the hard limit",
85            ));
86        }
87        Ok(())
88    }
89}
90
91/// 单个连接的准备、查询、证据时效和回收预算。
92#[derive(Clone, Debug, Deserialize, Serialize)]
93#[serde(default, deny_unknown_fields)]
94pub struct ConnectionPolicy {
95    /// 是否要求启动阶段确认成功;失败时阻止宿主完成启动。
96    pub startup_required: bool,
97    /// 一次请求的最大耗时,单位为毫秒。
98    pub request_timeout_ms: u64,
99    /// 后台轮询间隔,单位为毫秒,必须留出请求和证据时效预算。
100    pub poll_interval_ms: u64,
101    /// 发现证据的最大年龄,单位为毫秒。
102    pub max_age_ms: u64,
103    /// 查询异常后的旧证据宽限,单位为毫秒;零表示禁止异常复用。
104    pub stale_if_error_ms: u64,
105    /// 无人使用的服务观察保留时长,单位为毫秒。
106    pub idle_timeout_ms: u64,
107    /// 连接恢复的初始退避,单位为毫秒。
108    pub initial_backoff_ms: u64,
109    /// 连接恢复退避的上限,单位为毫秒。
110    pub max_backoff_ms: u64,
111}
112
113impl Default for ConnectionPolicy {
114    /// 业务作用:默认要求连接可用,并在查询异常时停止使用旧证据。
115    /// 参数说明:无。
116    /// 返回:操作、轮询、证据年龄与空闲回收的有限预算。
117    fn default() -> Self {
118        Self {
119            startup_required: true,
120            request_timeout_ms: 3000,
121            poll_interval_ms: 5000,
122            max_age_ms: 15000,
123            stale_if_error_ms: 0,
124            idle_timeout_ms: 30000,
125            initial_backoff_ms: 1000,
126            max_backoff_ms: 30000,
127        }
128    }
129}
130
131impl ConnectionPolicy {
132    /// 业务作用:校验一次操作与观察周期能否落在证据有效期内。
133    /// 参数说明:无。
134    /// 返回:预算不合理时在任何网络活动之前拒绝配置。
135    pub fn validate(&self) -> Result<()> {
136        for value in [
137            self.request_timeout_ms,
138            self.poll_interval_ms,
139            self.max_age_ms,
140            self.idle_timeout_ms,
141            self.initial_backoff_ms,
142            self.max_backoff_ms,
143        ] {
144            if value == 0 || value > 86_400_000 {
145                return Err(invalid("time budget is outside the supported range"));
146            }
147        }
148        if self.stale_if_error_ms > self.max_age_ms
149            || self.poll_interval_ms + self.request_timeout_ms >= self.max_age_ms
150            || self.initial_backoff_ms > self.max_backoff_ms
151        {
152            return Err(invalid("observation and retry budgets are inconsistent"));
153        }
154        Ok(())
155    }
156}
157
158/// 服务发布的首次确认要求、就绪影响和摘流预算。
159#[derive(Clone, Debug, Deserialize, Serialize)]
160#[serde(default, deny_unknown_fields)]
161pub struct RegistrationPolicy {
162    /// 是否要求启动阶段确认成功;失败时阻止宿主完成启动。
163    pub startup_required: bool,
164    /// 远端注册失去有效健康证据时是否影响宿主整体就绪。
165    pub readiness_critical: bool,
166    /// 注册健康回查间隔,单位为毫秒。
167    pub monitor_interval_ms: u64,
168    /// 注册健康证据的有效窗口,单位为毫秒。
169    pub observation_grace_ms: u64,
170    /// 单项注销预算,单位为毫秒,仍受宿主关闭总期限限制。
171    pub deregister_timeout_ms: u64,
172}
173
174impl Default for RegistrationPolicy {
175    /// 业务作用:区分首次发布确认与运行期整体就绪影响。
176    /// 参数说明:无。
177    /// 返回:首次必须确认、运行期默认降级的注册策略。
178    fn default() -> Self {
179        Self {
180            startup_required: true,
181            readiness_critical: false,
182            monitor_interval_ms: 5000,
183            observation_grace_ms: 15000,
184            deregister_timeout_ms: 3000,
185        }
186    }
187}
188
189impl RegistrationPolicy {
190    /// 业务作用:保证回查与注销不会使用无界或相互矛盾的期限。
191    /// 参数说明:`connection` 提供一次请求的最大预算。
192    /// 返回:合法策略可用于冻结计划;不合法策略拒绝发布。
193    pub fn validate(&self, connection: &ConnectionPolicy) -> Result<()> {
194        if self.monitor_interval_ms == 0
195            || self.deregister_timeout_ms == 0
196            || self.monitor_interval_ms > 86_400_000
197            || self.deregister_timeout_ms > 86_400_000
198            || self.observation_grace_ms > 86_400_000
199            || self.monitor_interval_ms + connection.request_timeout_ms >= self.observation_grace_ms
200        {
201            return Err(invalid("registration observation budgets are inconsistent"));
202        }
203        Ok(())
204    }
205}
206
207/// 业务作用:约束本地连接及注册标识,避免含混引用与无界诊断维度。
208/// 参数说明:`name` 是宿主提供的本地标识,不是后端服务名。
209/// 返回:符合有限 ASCII 名称规则时成功。
210pub fn validate_name(name: &str) -> Result<()> {
211    let bytes = name.as_bytes();
212    if bytes.is_empty()
213        || bytes.len() > 64
214        || !bytes[0].is_ascii_alphanumeric()
215        || !bytes
216            .iter()
217            .all(|b| b.is_ascii_alphanumeric() || *b == b'_' || *b == b'-')
218    {
219        return Err(invalid("invalid local discovery name"));
220    }
221    Ok(())
222}
223
224/// 业务作用:统一生成不含配置原文的预检错误。
225/// 参数说明:`reason` 是固定诊断原因。
226/// 返回:调用方可安全展示的配置错误。
227pub(crate) fn invalid(reason: &'static str) -> DiscoveryError {
228    DiscoveryError::new(ErrorCode::InvalidConfig, reason)
229}