mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
style: 修 rustfmt 格式化差异
This commit is contained in:
@@ -322,7 +322,12 @@ fn main() -> ExitCode {
|
||||
/// - watchdog spawn 脱离的 server 子进程,崩溃后经同一入口重启。
|
||||
fn server_command(command: ServerCommands) -> ExitCode {
|
||||
match command {
|
||||
ServerCommands::Start { rm, idle_timeout, workers, foreground } => {
|
||||
ServerCommands::Start {
|
||||
rm,
|
||||
idle_timeout,
|
||||
workers,
|
||||
foreground,
|
||||
} => {
|
||||
if rm {
|
||||
let mut config = RmServerConfig {
|
||||
idle_timeout: Duration::from_secs(idle_timeout),
|
||||
@@ -427,11 +432,7 @@ fn server_command(command: ServerCommands) -> ExitCode {
|
||||
/// 渲染 `ForgeStatus` 为多行文本:首行条目数,容量 / gc / 清缓存 / lookup
|
||||
/// 行按旗标结果附加。
|
||||
fn render_forge_status(
|
||||
target: ForgeTarget,
|
||||
entries: usize,
|
||||
capacity: usize,
|
||||
gc_collected: Option<usize>,
|
||||
cache_cleared: bool,
|
||||
target: ForgeTarget, entries: usize, capacity: usize, gc_collected: Option<usize>, cache_cleared: bool,
|
||||
lookup: Option<LookupResult>,
|
||||
) -> String {
|
||||
let mut lines = vec![format!("{} forge: {entries} entries", target.as_str())];
|
||||
@@ -573,10 +574,7 @@ fn restart_command() -> ExitCode {
|
||||
fn log_command(follow: bool, level: Option<Level>, lines: Option<usize>) -> ExitCode {
|
||||
let path = sidecar::well_known_log_path();
|
||||
if !path.exists() {
|
||||
eprintln!(
|
||||
"{}",
|
||||
Red.paint(format!("日志文件不存在:{}(server 可能未启动过)", path.display()))
|
||||
);
|
||||
eprintln!("{}", Red.paint(format!("日志文件不存在:{}(server 可能未启动过)", path.display())));
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,7 @@ use super::protocol::{ForgeTarget, LookupResult, ServerResponse};
|
||||
/// - --gc 重置各空闲 VM 的 session_bytes_allocated 为清扫后存活字节。
|
||||
/// - --clear-cache 清空字节码缓存(后续 eval 重编译)。
|
||||
pub fn handle_forge_query(
|
||||
target: ForgeTarget,
|
||||
gc: bool,
|
||||
clear_cache: bool,
|
||||
lookup: Option<String>,
|
||||
kernel: &Arc<KernelCore>,
|
||||
target: ForgeTarget, gc: bool, clear_cache: bool, lookup: Option<String>, kernel: &Arc<KernelCore>,
|
||||
pool: &Arc<VmPool>,
|
||||
) -> ServerResponse {
|
||||
// 读目标 forge 条目数与容量。
|
||||
@@ -140,7 +136,8 @@ mod tests {
|
||||
other => panic!("应得 ForgeStatus 帧,实得 {other:?}"),
|
||||
}
|
||||
|
||||
let response = handle_forge_query(ForgeTarget::String, false, false, Some("not-interned".into()), &kernel, &pool);
|
||||
let response =
|
||||
handle_forge_query(ForgeTarget::String, false, false, Some("not-interned".into()), &kernel, &pool);
|
||||
match response {
|
||||
ServerResponse::ForgeStatus { lookup, .. } => {
|
||||
assert_eq!(lookup, Some(LookupResult::Absent), "未 intern 键应得 Absent");
|
||||
|
||||
@@ -146,7 +146,8 @@ pub fn liveness_scan_with_waits(
|
||||
/// - 可能向 sidecar 记录的进程发 SIGTERM;可能删除 socket 与 sidecar 文件。
|
||||
/// - 杀进程决策与失败记入跨重启保留的日志文件。
|
||||
fn kill_zombie(
|
||||
id: &sidecar::ServerIdentity, sidecar_path: &Path, socket_path: &Path, zombie_exit_window: Duration, kill_wait: Duration,
|
||||
id: &sidecar::ServerIdentity, sidecar_path: &Path, socket_path: &Path, zombie_exit_window: Duration,
|
||||
kill_wait: Duration,
|
||||
) -> LivenessOutcome {
|
||||
// 有界等待 sidecar 消失:覆盖旧 server 退出序列窗口(排水 5 秒,sidecar
|
||||
// 最后删除)。消失即无操作,`claim_sidecar` 的 O_EXCL 创建成功。
|
||||
@@ -313,11 +314,8 @@ mod tests {
|
||||
let sidecar = dir.path("sidecar.json");
|
||||
let socket = dir.path("server.sock");
|
||||
let listener = UnixListener::bind(&socket).expect("绑定监听者应成功");
|
||||
sidecar::write_exclusive(
|
||||
&sidecar::ServerIdentity::new(socket.to_str().unwrap(), "0.0.0"),
|
||||
&sidecar,
|
||||
)
|
||||
.expect("写 sidecar 应成功");
|
||||
sidecar::write_exclusive(&sidecar::ServerIdentity::new(socket.to_str().unwrap(), "0.0.0"), &sidecar)
|
||||
.expect("写 sidecar 应成功");
|
||||
let outcome = liveness_scan(&sidecar, &socket);
|
||||
assert_eq!(outcome, LivenessOutcome::Noop, "存活 socket 应判无操作");
|
||||
assert!(sidecar.exists(), "sidecar 应原样");
|
||||
@@ -395,7 +393,8 @@ mod tests {
|
||||
sidecar::write_exclusive(&id, &sidecar).expect("写 sidecar 应成功");
|
||||
// 注入短僵尸退出窗口(500 毫秒)加短杀进程等待(300 毫秒):子进程忽略
|
||||
// SIGTERM,扫描判杀进程失败,文件原样。
|
||||
let outcome = liveness_scan_with_waits(&sidecar, &socket, Duration::from_millis(500), Duration::from_millis(300));
|
||||
let outcome =
|
||||
liveness_scan_with_waits(&sidecar, &socket, Duration::from_millis(500), Duration::from_millis(300));
|
||||
assert_eq!(outcome, LivenessOutcome::KillFailed, "忽略 SIGTERM 应判杀进程失败");
|
||||
assert!(sidecar.exists(), "sidecar 应原样");
|
||||
assert!(!socket.exists(), "socket 文件应保持不存在");
|
||||
|
||||
@@ -45,7 +45,11 @@ pub fn line_level(line: &str) -> Option<Level> {
|
||||
/// `ERROR`/`WARN`/`INFO` 行。
|
||||
pub fn filter_tail(lines: &[String], min_level: Option<Level>, last_n: Option<usize>) -> Vec<String> {
|
||||
let mut filtered: Vec<String> = match min_level {
|
||||
Some(min) => lines.iter().filter(|line| line_level(line).is_some_and(|level| level <= min)).cloned().collect(),
|
||||
Some(min) => lines
|
||||
.iter()
|
||||
.filter(|line| line_level(line).is_some_and(|level| level <= min))
|
||||
.cloned()
|
||||
.collect(),
|
||||
None => lines.to_vec(),
|
||||
};
|
||||
match last_n {
|
||||
@@ -85,7 +89,10 @@ pub fn follow(path: &Path, min_level: Option<Level>) {
|
||||
/// # 副作用
|
||||
/// - 向 writer 写行。
|
||||
fn follow_to<W: Write>(path: &Path, min_level: Option<Level>, writer: &mut W) {
|
||||
let mut file = OpenOptions::new().read(true).open(path).expect("日志文件应存在(调用方已检查)");
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.expect("日志文件应存在(调用方已检查)");
|
||||
let mut offset = file.seek(SeekFrom::End(0)).expect("定位日志文件末尾应成功");
|
||||
let mut buf = [0u8; 8192];
|
||||
let mut pending = String::new();
|
||||
@@ -176,11 +183,7 @@ mod tests {
|
||||
// info 留 ERROR/WARN/INFO 行,丢 DEBUG 行。
|
||||
assert_eq!(
|
||||
filter_tail(&lines, Some(Level::Info), None),
|
||||
vec![
|
||||
"1700000000.001 INFO a: one",
|
||||
"1700000000.002 ERROR a: two",
|
||||
"1700000000.004 WARN a: four",
|
||||
]
|
||||
vec!["1700000000.001 INFO a: one", "1700000000.002 ERROR a: two", "1700000000.004 WARN a: four",]
|
||||
);
|
||||
// debug 留 DEBUG 与更严重行(全留);trace 全留。
|
||||
assert_eq!(filter_tail(&lines, Some(Level::Debug), None), lines);
|
||||
|
||||
@@ -41,7 +41,7 @@ pub mod server;
|
||||
pub mod sidecar;
|
||||
/// spawn 与就绪探测:start、restart、watchdog 三条路径共用的唯一共享入口。
|
||||
pub mod spawn;
|
||||
/// worker 池:固定 N 常驻 worker、各持自有 VM 池、mpsc 轮询路由。
|
||||
pub mod workers;
|
||||
/// watchdog:前台监控进程,三态裁决加崩溃预算,崩溃后自动重启。
|
||||
pub mod watchdog;
|
||||
/// worker 池:固定 N 常驻 worker、各持自有 VM 池、mpsc 轮询路由。
|
||||
pub mod workers;
|
||||
|
||||
@@ -201,12 +201,20 @@ impl ServerResponse {
|
||||
}
|
||||
|
||||
/// 请求帧可取的 `type` 值(须与 `ServerRequest` 变体保持同步)。
|
||||
const REQUEST_TAGS: &[&str] =
|
||||
&["eval", "forge_query", "version", "status", "health", "info", "shutdown", "yield"];
|
||||
const REQUEST_TAGS: &[&str] = &["eval", "forge_query", "version", "status", "health", "info", "shutdown", "yield"];
|
||||
|
||||
/// 响应帧可取的 `type` 值(须与 `ServerResponse` 变体保持同步)。
|
||||
const RESPONSE_TAGS: &[&str] =
|
||||
&["eval_result", "forge_status", "version", "status", "health", "info", "shutdown", "yield", "error"];
|
||||
const RESPONSE_TAGS: &[&str] = &[
|
||||
"eval_result",
|
||||
"forge_status",
|
||||
"version",
|
||||
"status",
|
||||
"health",
|
||||
"info",
|
||||
"shutdown",
|
||||
"yield",
|
||||
"error",
|
||||
];
|
||||
|
||||
/// 序列化请求为帧字符串(含结尾换行)。
|
||||
///
|
||||
|
||||
@@ -208,9 +208,7 @@ pub fn run_server(config: &ServerConfig) -> Result<(), ServerError> {
|
||||
// 命令。
|
||||
match liveness::liveness_scan(&config.sidecar_path, &config.socket_path) {
|
||||
liveness::LivenessOutcome::Noop | liveness::LivenessOutcome::Cleaned => {}
|
||||
liveness::LivenessOutcome::KillFailed => {
|
||||
return Err(ServerError::ClaimRefused(ClaimResult::RefusedAmbiguous))
|
||||
}
|
||||
liveness::LivenessOutcome::KillFailed => return Err(ServerError::ClaimRefused(ClaimResult::RefusedAmbiguous)),
|
||||
}
|
||||
|
||||
// 认领 sidecar:同版本拒绝启动,异版本走交接路径,存活证据不足即返回错误。
|
||||
@@ -1208,11 +1206,17 @@ mod tests {
|
||||
fn yield_takeover_end_to_end() {
|
||||
let config = unique_paths("yield_e2e");
|
||||
// A 用版本 0.0.1。
|
||||
let config_a = ServerConfig { version: "0.0.1".to_string(), ..config.clone() };
|
||||
let config_a = ServerConfig {
|
||||
version: "0.0.1".to_string(),
|
||||
..config.clone()
|
||||
};
|
||||
let handle_a = start_server(config_a);
|
||||
|
||||
// B 用版本 0.0.2:认领得 VersionMismatch 转交接路径。
|
||||
let config_b = ServerConfig { version: "0.0.2".to_string(), ..config.clone() };
|
||||
let config_b = ServerConfig {
|
||||
version: "0.0.2".to_string(),
|
||||
..config.clone()
|
||||
};
|
||||
let handle_b = std::thread::spawn(move || run_server(&config_b));
|
||||
|
||||
// A 应在 30 秒内有界退出(排水加退出序列)。
|
||||
|
||||
@@ -30,10 +30,7 @@ pub fn spawn_detached_server(workers: Option<u32>) -> std::io::Result<std::proce
|
||||
if let Some(n) = workers {
|
||||
cmd.args(["--workers", &n.to_string()]);
|
||||
}
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()).spawn()
|
||||
}
|
||||
|
||||
/// 就绪探测:轮询健康请求直至得 healthy。
|
||||
|
||||
@@ -99,10 +99,7 @@ impl WorkerRouter {
|
||||
/// # 注意事项
|
||||
/// - 返回的句柄必须在关闭序列中逐一 join(drop 发送端 → join → drop 内核)。
|
||||
pub fn spawn_workers(
|
||||
kernel: Arc<KernelCore>,
|
||||
counters: Arc<PoolCounters>,
|
||||
min_pool_size: usize,
|
||||
max_pool_size: Option<usize>,
|
||||
kernel: Arc<KernelCore>, counters: Arc<PoolCounters>, min_pool_size: usize, max_pool_size: Option<usize>,
|
||||
worker_count: usize,
|
||||
) -> (WorkerRouter, Vec<JoinHandle<()>>) {
|
||||
let n = worker_count.max(1);
|
||||
@@ -126,11 +123,7 @@ pub fn spawn_workers(
|
||||
fn worker_loop(receiver: mpsc::Receiver<WorkerTask>, kernel: Arc<KernelCore>, pool: Arc<VmPool>) {
|
||||
for task in receiver {
|
||||
match task {
|
||||
WorkerTask::Eval {
|
||||
code,
|
||||
max_steps,
|
||||
reply,
|
||||
} => {
|
||||
WorkerTask::Eval { code, max_steps, reply } => {
|
||||
let response = eval::handle_eval(&code, max_steps, &kernel, &pool);
|
||||
let _ = reply.send(response);
|
||||
}
|
||||
|
||||
@@ -81,8 +81,7 @@ fn server_help_lists_subcommands() {
|
||||
assert!(output.status.success(), "server --help 应退 0");
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for name in [
|
||||
"start", "stop", "status", "health", "info", "version", "cleanup", "restart", "log", "forge",
|
||||
"watchdog",
|
||||
"start", "stop", "status", "health", "info", "version", "cleanup", "restart", "log", "forge", "watchdog",
|
||||
] {
|
||||
assert!(stdout.contains(name), "help 应列出 {name}:{stdout}");
|
||||
}
|
||||
@@ -124,10 +123,7 @@ fn server_control_arms_no_server_exit_1() {
|
||||
let output = oxide(&["server", sub]);
|
||||
assert_eq!(output.status.code(), Some(1), "{sub} 应退 1");
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(
|
||||
stderr.contains("无已注册的 server"),
|
||||
"{sub} 应打印无 server 提示:{stderr}"
|
||||
);
|
||||
assert!(stderr.contains("无已注册的 server"), "{sub} 应打印无 server 提示:{stderr}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,10 +141,7 @@ fn server_forge_no_server_exit_1() {
|
||||
let output = oxide(&["server", "forge", "code"]);
|
||||
assert_eq!(output.status.code(), Some(1), "forge 应退 1");
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(
|
||||
stderr.contains("无已注册的 server"),
|
||||
"forge 应打印无 server 提示:{stderr}"
|
||||
);
|
||||
assert!(stderr.contains("无已注册的 server"), "forge 应打印无 server 提示:{stderr}");
|
||||
}
|
||||
|
||||
/// cleanup 退 0(幂等:有残留则清理、无残留则报无残留),事后全局路径无文件。
|
||||
|
||||
@@ -53,8 +53,7 @@ fn control_client_end_to_end() {
|
||||
// 状态请求:worker 池异步预热,轮询至总数不小于 1 后核对字段。
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let response = loop {
|
||||
let response =
|
||||
send_control_request_from(&config.sidecar_path, &ServerRequest::Status).expect("状态请求应成功");
|
||||
let response = send_control_request_from(&config.sidecar_path, &ServerRequest::Status).expect("状态请求应成功");
|
||||
if let ServerResponse::Status { pool_total, .. } = &response {
|
||||
if *pool_total >= 1 {
|
||||
break response;
|
||||
@@ -89,12 +88,8 @@ fn control_client_end_to_end() {
|
||||
}
|
||||
|
||||
// 关闭请求:得确认帧,server 线程正常退出,socket 与 sidecar 文件均被删。
|
||||
let response =
|
||||
send_control_request_from(&config.sidecar_path, &ServerRequest::Shutdown).expect("关闭请求应成功");
|
||||
assert!(
|
||||
matches!(response, ServerResponse::Shutdown),
|
||||
"应得关闭确认帧:{response:?}"
|
||||
);
|
||||
let response = send_control_request_from(&config.sidecar_path, &ServerRequest::Shutdown).expect("关闭请求应成功");
|
||||
assert!(matches!(response, ServerResponse::Shutdown), "应得关闭确认帧:{response:?}");
|
||||
let result = handle.join().expect("server 线程应正常退出");
|
||||
assert!(result.is_ok(), "server 应正常退出:{result:?}");
|
||||
assert!(!config.socket_path.exists(), "socket 文件应被删除");
|
||||
|
||||
@@ -13,11 +13,7 @@ use oxide_cli::server::server::{run_server, ServerConfig, ServerError};
|
||||
/// 唯一临时路径(进程号加测试名):单进程内唯一,进程间以进程号隔离。
|
||||
/// worker 数取小值 2:与既有单测同口径,大值徒增预热成本。
|
||||
fn unique_paths(test_name: &str) -> ServerConfig {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"oxide_server_signal_int_{}_{}",
|
||||
std::process::id(),
|
||||
test_name
|
||||
));
|
||||
let dir = std::env::temp_dir().join(format!("oxide_server_signal_int_{}_{}", std::process::id(), test_name));
|
||||
std::fs::create_dir_all(&dir).expect("测试目录创建应成功");
|
||||
ServerConfig {
|
||||
socket_path: dir.join("server.sock"),
|
||||
|
||||
@@ -13,11 +13,7 @@ use oxide_cli::server::server::{run_server, ServerConfig, ServerError};
|
||||
/// 唯一临时路径(进程号加测试名):单进程内唯一,进程间以进程号隔离。
|
||||
/// worker 数取小值 2:与既有单测同口径,大值徒增预热成本。
|
||||
fn unique_paths(test_name: &str) -> ServerConfig {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"oxide_server_signal_term_{}_{}",
|
||||
std::process::id(),
|
||||
test_name
|
||||
));
|
||||
let dir = std::env::temp_dir().join(format!("oxide_server_signal_term_{}_{}", std::process::id(), test_name));
|
||||
std::fs::create_dir_all(&dir).expect("测试目录创建应成功");
|
||||
ServerConfig {
|
||||
socket_path: dir.join("server.sock"),
|
||||
|
||||
Reference in New Issue
Block a user