Skip to main content

nadisc_runtime/
lib.rs

1//! 通用多注册中心 owner。宿主决定本地接流资格,owner 管理观察、注册和有界关闭。
2//! 连接通过 qualifier 精确选择,默认名为 primary;注册清单单独决定发布目标。
3//! 连接失败不跨环境回退,提交点复验证据,关闭报告保留结果不明的远端责任。
4//! 发现句柄只有读权;关闭等待者的取消不会撤销已经发起的清理。
5
6pub mod assembly;
7pub mod config;
8mod observation;
9mod owner;
10pub mod providers;
11mod registration;
12mod status;
13mod transport;
14pub use status::{ConnectionStatus, RuntimeObserver, RuntimeStatus};
15pub use transport::TransportLease;
16
17pub use nadisc::managed;
18pub use observation::{DiscoveryHandle, Observation, Snapshot};
19pub use owner::{
20    CloseReport, CloseTicket, ConnectionSpec, DiscoveryOwner, OwnerBuilder, OwnerPhase,
21    QuiesceTicket,
22};
23pub use registration::{
24    Eligibility, EligibilityController, RegistrationSpec, RegistrationState, RegistrationStatus,
25};
26
27use nadisc::managed::{DiscoveryError, ErrorCode, ManagedProvider, Result};
28use std::{
29    collections::BTreeMap,
30    sync::{
31        atomic::{AtomicU64, Ordering},
32        Arc, Mutex, MutexGuard,
33    },
34    time::Duration,
35};
36use tokio::{
37    sync::{watch, Semaphore},
38    task::JoinHandle,
39    time::Instant,
40};
41use tokio_util::sync::CancellationToken;
42
43pub(crate) struct Core {
44    pub id: String,
45    pub gate: Mutex<Gate>,
46    pub connections: BTreeMap<String, Arc<Connection>>,
47    pub default_connection: Option<String>,
48    pub limits: config::Limits,
49    pub quota: Arc<Mutex<Quota>>,
50    pub operations: Semaphore,
51    pub watches: Semaphore,
52    pub tasks: Mutex<Vec<JoinHandle<()>>>,
53    pub registration_tasks: Mutex<Vec<JoinHandle<()>>>,
54    pub withdrawal_task: Mutex<Option<JoinHandle<()>>>,
55    pub close_task: Mutex<Option<JoinHandle<()>>>,
56    pub bridges: Arc<Semaphore>,
57    pub transports: Arc<Semaphore>,
58    pub stop: CancellationToken,
59    pub registrations_stop: CancellationToken,
60    pub changes: watch::Sender<u64>,
61    pub close: watch::Sender<Option<CloseReport>>,
62    pub withdrawn: watch::Sender<Option<Vec<RegistrationStatus>>>,
63}
64
65pub(crate) struct Gate {
66    pub phase: OwnerPhase,
67    pub registrations: BTreeMap<String, Arc<registration::Record>>,
68    pub frozen: bool,
69    pub close_started: bool,
70    pub failed: bool,
71    pub shutdown_deadline: Option<Instant>,
72}
73
74pub(crate) struct Connection {
75    pub name: String,
76    pub provider: Arc<dyn ManagedProvider>,
77    pub policy: config::ConnectionPolicy,
78    pub slots: Mutex<BTreeMap<String, Arc<observation::Slot>>>,
79    pub prepared: watch::Sender<bool>,
80    pub error: Mutex<Option<DiscoveryError>>,
81    pub epoch: AtomicU64,
82    pub revocation: AtomicU64,
83    pub queries: AtomicU64,
84    pub query_errors: AtomicU64,
85    pub query_micros: AtomicU64,
86}
87
88#[derive(Default)]
89pub(crate) struct Quota {
90    instances: usize,
91    bytes: usize,
92}
93
94/// 业务作用:在内存状态锁被中断后仍允许执行撤权和清理。
95///
96/// 参数说明:
97/// - `mutex`:仅保护短时内存操作,不得跨网络 await 持有。
98///
99/// 返回:状态保护锁;业务错误由显式状态字段表达。
100pub(crate) fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
101    mutex
102        .lock()
103        .unwrap_or_else(std::sync::PoisonError::into_inner)
104}
105
106/// 业务作用:将有限毫秒配置转换为 Tokio 使用的持续时间。
107///
108/// 参数说明:
109/// - `value`:已经过策略预检。
110///
111/// 返回:对应持续时间。
112pub(crate) fn millis(value: u64) -> Duration {
113    Duration::from_millis(value)
114}
115
116/// 业务作用:向所有状态等待者发布不携带配置内容的变更通知。
117///
118/// 参数说明:
119/// - `core`:是共享状态,不代表资源关闭权。
120///
121/// 返回:等待者可重新检查当前事实,不依赖中间通知逐条到达。
122pub(crate) fn changed(core: &Core) {
123    core.changes
124        .send_modify(|sequence| *sequence = sequence.wrapping_add(1));
125}
126
127/// 业务作用:限制单次操作的排队与执行总时间,并响应最终关闭。
128///
129/// 参数说明:
130/// - `core`:提供并发额度
131/// - `deadline`:是总截止点
132/// - `operation`:是一次后端操作。
133///
134/// 返回:额度等待和网络操作共享期限;关闭或超时返回稳定错误。
135pub(crate) async fn operation<T>(
136    core: &Core,
137    deadline: Instant,
138    operation: impl std::future::Future<Output = Result<T>>,
139) -> Result<T> {
140    if Instant::now() >= deadline {
141        return Err(timeout());
142    }
143    tokio::select! {
144        biased;
145        _ = core.stop.cancelled() => Err(closed()),
146        result = tokio::time::timeout_at(deadline, async {
147            let _permit = core.operations.acquire().await.map_err(|_| closed())?;
148            tokio::pin!(operation);
149            // 排队和后端挂起都可能跨过期限,不能让恰好 Ready 的迟到操作绕过超时裁决。
150            futures_util::future::poll_fn(|context| {
151                if Instant::now() >= deadline { std::task::Poll::Ready(Err(timeout())) }
152                else { operation.as_mut().poll(context) }
153            }).await
154        }) => result.unwrap_or_else(|_| Err(timeout())),
155    }
156}
157
158/// 业务作用:在监督边界捕获后台任务异常并同步撤销所有发现资格。
159///
160/// 参数说明:
161/// - `core`:接管任务
162/// - `future`:必须服从 owner 取消和有限网络期限。
163///
164/// 返回:任务归 owner 持有;异常不会留下仍被认为健康的观察。
165pub(crate) fn spawn_owned(
166    core: &Arc<Core>,
167    future: impl std::future::Future<Output = ()> + Send + 'static,
168) -> tokio::task::AbortHandle {
169    spawn_task(core, false, future)
170}
171
172/// 业务作用:登记会在摘流阶段单独停止的注册任务。
173///
174/// 参数说明:
175/// - `core`:接管任务
176/// - `future`:只处理本 owner 的注册记录。
177///
178/// 返回:任务可在保留出站发现时被停止并回收。
179pub(crate) fn spawn_registration(
180    core: &Arc<Core>,
181    future: impl std::future::Future<Output = ()> + Send + 'static,
182) {
183    spawn_task(core, true, future);
184}
185
186/// 业务作用:按关闭阶段登记任务,并在异常退出时撤销共享健康事实。
187///
188/// 参数说明:
189/// - `core`:提供监督边界
190/// - `registration`:指定摘流阶段
191/// - `future`:是有界任务。
192///
193/// 返回:任务持有共享状态,不持有控制 owner。
194fn spawn_task(
195    core: &Arc<Core>,
196    registration: bool,
197    future: impl std::future::Future<Output = ()> + Send + 'static,
198) -> tokio::task::AbortHandle {
199    use futures_util::FutureExt;
200    let shared = core.clone();
201    let task = tokio::spawn(async move {
202        if std::panic::AssertUnwindSafe(future)
203            .catch_unwind()
204            .await
205            .is_err()
206        {
207            // 监督失效后不能继续根据最后成功值发布路由。
208            let mut gate = lock(&shared.gate);
209            gate.failed = true;
210            gate.phase = OwnerPhase::Quiescing;
211            shared.stop.cancel();
212            shared.registrations_stop.cancel();
213            drop(gate);
214            changed(&shared);
215        }
216    });
217    let mut tasks = lock(if registration {
218        &core.registration_tasks
219    } else {
220        &core.tasks
221    });
222    tasks.retain(|task| !task.is_finished());
223    let abort = task.abort_handle();
224    tasks.push(task);
225    abort
226}
227
228/// 业务作用:表达已经关闭或失去监督的资源不可继续接纳操作。
229///
230/// 参数说明:无。
231///
232/// 返回:不包含后端细节的稳定错误。
233pub(crate) fn closed() -> DiscoveryError {
234    DiscoveryError::new(ErrorCode::OwnerClosed, "discovery owner is closed")
235}
236
237/// 业务作用:统一表示共享期限耗尽。
238///
239/// 参数说明:无。
240///
241/// 返回:可由上层识别的期限错误。
242pub(crate) fn timeout() -> DiscoveryError {
243    DiscoveryError::new(ErrorCode::DeadlineExceeded, "discovery deadline elapsed")
244}
245
246/// 业务作用:错开不同进程和连接的恢复请求,同时保留有限退避上界。
247///
248/// 参数说明:
249/// - `core`:提供进程实例身份
250/// - `connection`:限定连接
251/// - `maximum_ms`:是当前指数上界
252/// - `attempt`:是本地尝试序号。
253///
254/// 返回:位于上界一半到上界之间的毫秒时长,不读取业务数据或秘密。
255pub(crate) fn retry_delay(
256    core: &Core,
257    connection: &Connection,
258    maximum_ms: u64,
259    attempt: u64,
260) -> Duration {
261    use std::hash::{Hash, Hasher};
262    let mut hash = std::collections::hash_map::DefaultHasher::new();
263    (&core.id, &connection.name, attempt).hash(&mut hash);
264    let floor = maximum_ms.div_ceil(2);
265    millis(floor + hash.finish() % (maximum_ms - floor + 1))
266}
267
268/// 业务作用:使同一连接的认证撤销同步影响发现与注册健康证据,并通知宿主更新就绪。
269///
270/// 参数说明:
271/// - `core`:提供状态通知
272/// - `gate`:必须是其已持有的门禁
273/// - `connection`:是发生身份撤销的连接。
274///
275/// 返回:递增权限撤销计数并撤销已有资格;其他连接不受影响,旧代迟到结果不能恢复健康。
276pub(crate) fn revoke_connection(core: &Core, gate: &Gate, connection: &Connection) {
277    connection.revocation.fetch_add(1, Ordering::AcqRel);
278    for slot in lock(&connection.slots).values() {
279        let mut state = lock(&slot.state);
280        // None 还表示静态证据无期限;明确记为已过期,后续失败只能收紧而不能从旧 observed_at 重新生成宽限。
281        state.valid_until = Some(Instant::now());
282        state.error = Some(DiscoveryError::new(
283            ErrorCode::Unauthenticated,
284            "connection identity is unavailable",
285        ));
286        slot.changes
287            .send_modify(|value| *value = value.wrapping_add(1));
288    }
289    // 先在共同门禁内撤销该身份覆盖的全部注册,再通知宿主;不能等待各注册下一次网络回查。
290    for record in gate
291        .registrations
292        .values()
293        .filter(|record| std::ptr::eq(record.connection.as_ref(), connection))
294    {
295        let mut status = lock(&record.status);
296        status.valid_until = None;
297        if matches!(
298            status.state,
299            RegistrationState::Active
300                | RegistrationState::PendingHealth
301                | RegistrationState::Suspended
302        ) {
303            status.state = RegistrationState::Suspended;
304            status.error = Some(DiscoveryError::new(
305                ErrorCode::Unauthenticated,
306                "registration identity is unavailable",
307            ));
308        }
309    }
310    changed(core);
311}