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