Skip to main content

nichlink_plugin_host/
deployment.rs

1//! Atomic deployment of registration metadata plus executable code.
2//! 注册元数据与可执行代码的原子部署。
3
4use std::sync::{Arc, Mutex};
5
6use arc_swap::ArcSwap;
7use nichlink_run_method::{GraftPlan, Registry};
8
9use crate::{HostError, PluginInstance};
10
11/// One consistent view of registration metadata and executable code.
12/// 注册元数据与可执行代码的一致视图。
13pub struct Deployment<I> {
14    /// Monotonic counter; compare successive snapshots to notice a swap. Starts at 0.
15    /// 单调递增的计数器;比较前后快照即可察觉替换。初始为 0。
16    pub generation: u64,
17    /// Registry snapshot that matches `plugin`.
18    /// 与 `plugin` 相匹配的注册树快照。
19    pub registry: Registry,
20    /// Plugin instance serving this snapshot; the `Arc` keeps it alive for readers.
21    /// 服务该快照的插件实例;`Arc` 让读者持有期间其保持存活。
22    pub plugin: Arc<I>,
23}
24
25/// Lock-free reads with serialized, validated replacement.
26/// 读取无锁,替换串行校验。
27pub struct HotDeployment<I> {
28    current: ArcSwap<Deployment<I>>,
29    writer: Mutex<()>,
30}
31
32impl<I: PluginInstance> HotDeployment<I> {
33    /// Install generation 0 after the plugin passes its health probe.
34    /// 在插件通过健康探针后安装第 0 代。
35    pub fn new(registry: Registry, plugin: I) -> Result<Self, HostError> {
36        plugin.health_check()?;
37        Ok(Self {
38            current: ArcSwap::from_pointee(Deployment {
39                generation: 0,
40                registry,
41                plugin: Arc::new(plugin),
42            }),
43            writer: Mutex::new(()),
44        })
45    }
46
47    /// Return the current snapshot; readers never block a writer.
48    /// 返回当前快照;读者不会阻塞写者。
49    pub fn load(&self) -> Arc<Deployment<I>> {
50        self.current.load_full()
51    }
52
53    /// Validate code and registry graft before publishing one new generation.
54    /// 发布新代之前,先完成代码体检和注册树嫁接校验。
55    pub fn replace(
56        &self,
57        plugin: I,
58        plan: &GraftPlan,
59        external: &Registry,
60    ) -> Result<u64, HostError> {
61        plugin.health_check()?;
62        let _writer = self
63            .writer
64            .lock()
65            .map_err(|_| HostError::Process("deployment writer lock was poisoned".to_owned()))?;
66        let live = self.current.load_full();
67        let registry = live
68            .registry
69            .overlay(plan, external)
70            .map_err(|error| HostError::Registry(error.to_string()))?;
71        let generation = live.generation.saturating_add(1);
72        self.current.store(Arc::new(Deployment {
73            generation,
74            registry,
75            plugin: Arc::new(plugin),
76        }));
77        Ok(generation)
78    }
79}