fix: 修复3个P0遗留 — AutoRollback回退/ConfigReloaded序列化/FfiString跨allocator
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::core::config::AppConfig;
|
||||
use crate::core::message::{Destination, Envelope, Message};
|
||||
use crate::core::plugin::{CapabilityTestResult, Plugin, PluginContext};
|
||||
use crate::core::plugin_loader::ErrorPolicy;
|
||||
use crate::core::version_manager::VersionManager;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{mpsc, Arc};
|
||||
@@ -27,6 +28,8 @@ struct PluginState {
|
||||
required_capabilities: Vec<String>,
|
||||
/// 是否自动测试
|
||||
auto_test: bool,
|
||||
/// 是否需要在后续生命周期中执行回退
|
||||
needs_rollback: bool,
|
||||
}
|
||||
|
||||
impl PluginState {
|
||||
@@ -42,6 +45,7 @@ impl PluginState {
|
||||
capabilities: vec![],
|
||||
required_capabilities: vec![],
|
||||
auto_test: false, // 静态插件默认不自测
|
||||
needs_rollback: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +61,7 @@ impl PluginState {
|
||||
capabilities: vec![],
|
||||
required_capabilities: vec![],
|
||||
auto_test: true,
|
||||
needs_rollback: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +88,7 @@ pub struct ServiceManager {
|
||||
tx: mpsc::Sender<Envelope>,
|
||||
rx: mpsc::Receiver<Envelope>,
|
||||
running: bool,
|
||||
version_manager: Option<VersionManager>,
|
||||
}
|
||||
|
||||
impl ServiceManager {
|
||||
@@ -94,9 +100,14 @@ impl ServiceManager {
|
||||
tx,
|
||||
rx,
|
||||
running: false,
|
||||
version_manager: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_version_manager(&mut self, version_manager: VersionManager) {
|
||||
self.version_manager = Some(version_manager);
|
||||
}
|
||||
|
||||
/// 注册静态插件(编译时链接的插件)
|
||||
pub fn register(&mut self, plugin: Box<dyn Plugin>) {
|
||||
println!("[ServiceManager] 注册插件: {}", plugin.id());
|
||||
@@ -351,10 +362,49 @@ impl ServiceManager {
|
||||
enabled: s.enabled,
|
||||
test_results: s.test_results.clone(),
|
||||
capabilities: s.capabilities.clone(),
|
||||
needs_rollback: s.needs_rollback,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn replace_dynamic_plugin_at_index(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
plugin_id: &str,
|
||||
new_plugin: Box<dyn Plugin>,
|
||||
error_policy: ErrorPolicy,
|
||||
max_errors: u32,
|
||||
required_capabilities: Vec<String>,
|
||||
capabilities: Vec<String>,
|
||||
auto_test: bool,
|
||||
) -> Result<()> {
|
||||
if !self.plugins[idx].is_dynamic {
|
||||
return Err(anyhow!(
|
||||
"plugin '{plugin_id}' is not dynamic and cannot be replaced"
|
||||
));
|
||||
}
|
||||
|
||||
let mut new_state = PluginState::new_dynamic(new_plugin, error_policy, max_errors);
|
||||
new_state.required_capabilities = required_capabilities;
|
||||
new_state.capabilities = capabilities;
|
||||
new_state.auto_test = auto_test;
|
||||
|
||||
let ctx = PluginContext {
|
||||
tx: self.tx.clone(),
|
||||
config: Arc::clone(&self.config),
|
||||
};
|
||||
new_state.plugin.init(ctx)?;
|
||||
new_state.plugin.start()?;
|
||||
|
||||
if self.plugins[idx].enabled {
|
||||
let _ = self.plugins[idx].plugin.stop();
|
||||
}
|
||||
|
||||
self.plugins[idx] = new_state;
|
||||
println!("[ServiceManager] 插件 '{plugin_id}' 热替换成功");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 热替换动态插件(stop 旧的 → 替换 → init → start 新的)
|
||||
pub fn replace_dynamic_plugin(
|
||||
&mut self,
|
||||
@@ -374,22 +424,21 @@ impl ServiceManager {
|
||||
"plugin '{plugin_id}' is not dynamic and cannot be replaced"
|
||||
));
|
||||
}
|
||||
let mut new_state = PluginState::new_dynamic(new_plugin, error_policy, max_errors);
|
||||
|
||||
let ctx = PluginContext {
|
||||
tx: self.tx.clone(),
|
||||
config: Arc::clone(&self.config),
|
||||
};
|
||||
new_state.plugin.init(ctx)?;
|
||||
new_state.plugin.start()?;
|
||||
let required_capabilities = self.plugins[idx].required_capabilities.clone();
|
||||
let capabilities = self.plugins[idx].capabilities.clone();
|
||||
let auto_test = self.plugins[idx].auto_test;
|
||||
|
||||
if self.plugins[idx].enabled {
|
||||
let _ = self.plugins[idx].plugin.stop();
|
||||
}
|
||||
|
||||
self.plugins[idx] = new_state;
|
||||
println!("[ServiceManager] 插件 '{plugin_id}' 热替换成功");
|
||||
Ok(())
|
||||
self.replace_dynamic_plugin_at_index(
|
||||
idx,
|
||||
plugin_id,
|
||||
new_plugin,
|
||||
error_policy,
|
||||
max_errors,
|
||||
required_capabilities,
|
||||
capabilities,
|
||||
auto_test,
|
||||
)
|
||||
}
|
||||
|
||||
/// 处理发给管理层自身的消息
|
||||
@@ -424,7 +473,7 @@ impl ServiceManager {
|
||||
let new_config = Arc::new(new_config);
|
||||
self.config = Arc::clone(&new_config);
|
||||
println!("[ServiceManager] 配置重载成功,广播 ConfigReloaded");
|
||||
self.broadcast_message(Message::ConfigReloaded(new_config));
|
||||
self.broadcast_message(Message::ConfigReloaded((*new_config).clone()));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ServiceManager] 配置重载失败: {}", e);
|
||||
@@ -588,28 +637,102 @@ impl ServiceManager {
|
||||
|
||||
/// 插件错误达到阈值时的处理
|
||||
fn handle_error_threshold(&mut self, plugin_id: &str) {
|
||||
let state = match self.plugins.iter_mut().find(|s| s.id() == plugin_id) {
|
||||
Some(s) => s,
|
||||
let idx = match self.plugins.iter().position(|s| s.id() == plugin_id) {
|
||||
Some(idx) => idx,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match state.error_policy {
|
||||
match self.plugins[idx].error_policy.clone() {
|
||||
ErrorPolicy::DisableAndLog => {
|
||||
eprintln!(
|
||||
"[ServiceManager] 插件 '{}' 错误次数达到阈值,已禁用",
|
||||
plugin_id
|
||||
);
|
||||
let state = &mut self.plugins[idx];
|
||||
let _ = state.plugin.stop();
|
||||
state.enabled = false;
|
||||
state.needs_rollback = false;
|
||||
}
|
||||
ErrorPolicy::AutoRollback => {
|
||||
{
|
||||
let state = &mut self.plugins[idx];
|
||||
let _ = state.plugin.stop();
|
||||
state.enabled = false;
|
||||
state.needs_rollback = false;
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"[ServiceManager] 插件 '{}' 错误次数达到阈值,需要回退 (由外部 VersionManager 处理)",
|
||||
"[ServiceManager] 插件 '{}' 错误次数达到阈值,尝试自动回退到稳定版本",
|
||||
plugin_id
|
||||
);
|
||||
// 先禁用,等待外部 (main.rs / HTTP API) 调用 VersionManager 执行回退
|
||||
let _ = state.plugin.stop();
|
||||
state.enabled = false;
|
||||
|
||||
let rollback_result = {
|
||||
let Some(version_manager) = self.version_manager.as_ref() else {
|
||||
eprintln!(
|
||||
"[ServiceManager] 插件 '{}' 未配置 VersionManager,标记为待回退",
|
||||
plugin_id
|
||||
);
|
||||
self.plugins[idx].needs_rollback = true;
|
||||
return;
|
||||
};
|
||||
|
||||
match version_manager.rollback(plugin_id) {
|
||||
Ok(version) => match version_manager
|
||||
.loader()
|
||||
.load_plugin(plugin_id, Some(&version))
|
||||
{
|
||||
Ok((plugin, manifest)) => {
|
||||
Ok((version, Box::new(plugin) as Box<dyn Plugin>, manifest))
|
||||
}
|
||||
Err(e) => Err((Some(version), e)),
|
||||
},
|
||||
Err(e) => Err((None, e)),
|
||||
}
|
||||
};
|
||||
|
||||
match rollback_result {
|
||||
Ok((version, plugin, manifest)) => {
|
||||
let max_errors = self.plugins[idx].max_errors;
|
||||
match self.replace_dynamic_plugin_at_index(
|
||||
idx,
|
||||
plugin_id,
|
||||
plugin,
|
||||
manifest.error_policy,
|
||||
max_errors,
|
||||
manifest.required_capabilities,
|
||||
manifest.capabilities,
|
||||
manifest.auto_test,
|
||||
) {
|
||||
Ok(()) => {
|
||||
println!(
|
||||
"[ServiceManager] 插件 '{}' 已回退并重新加载稳定版本 {}",
|
||||
plugin_id, version
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[ServiceManager] 插件 '{}' 已切换到稳定版本 {},但热替换失败: {}",
|
||||
plugin_id, version, e
|
||||
);
|
||||
self.plugins[idx].needs_rollback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err((Some(version), e)) => {
|
||||
eprintln!(
|
||||
"[ServiceManager] 插件 '{}' 已切换到稳定版本 {},但加载回退版本失败: {}",
|
||||
plugin_id, version, e
|
||||
);
|
||||
self.plugins[idx].needs_rollback = true;
|
||||
}
|
||||
Err((None, e)) => {
|
||||
eprintln!(
|
||||
"[ServiceManager] 插件 '{}' 自动回退失败,标记为待回退: {}",
|
||||
plugin_id, e
|
||||
);
|
||||
self.plugins[idx].needs_rollback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,4 +755,5 @@ pub struct PluginStateInfo {
|
||||
pub enabled: bool,
|
||||
pub test_results: Vec<CapabilityTestResult>,
|
||||
pub capabilities: Vec<String>,
|
||||
pub needs_rollback: bool,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user