core: review fixes, init builds the agent, console without a terminal
Activation path (review T6R): a broken plugin or a stray file in /plugins blocks no activation; candidates replace their own entries; discovery has a 10 s deadline and its process is stopped after the answer; an unchanged hash keeps plugin.prev.wasm; activations run in their own task; the watcher is not recursive; no duplicate services; rpc.retired waits for live processes of its version; reads and hashes on blocking threads. Guest I/O and per-call paths: a stdin write error is EOF, stdout and stderr are drained before the status; stderr is lossy UTF-8 and goes to the journal from its own reader; compiled components are freed; shutdown starts no new processes; lock queues drop stopping processes; compile before the admission gate; CommandPre per component; core.unlock in order; null errors are successes; the journal thread survives SQLite errors. sausage init builds the agent with cargo.build; sausage without a command creates ./world. The console reads plain lines without a terminal. PROTOCOL.md and DECISIONS.md describe the decided rules. The flake tests the whole workspace and checks the packaged cargo.build.
This commit is contained in:
+47
-7
@@ -1,7 +1,7 @@
|
||||
//! Grace periods, service restarts, and retirement of replaced versions (PROTOCOL.md section 3.7).
|
||||
use crate::{Host, services::Service};
|
||||
use serde_json::json;
|
||||
use std::{collections::BTreeMap, time::Duration};
|
||||
use std::{collections::BTreeMap, sync::atomic::Ordering, time::Duration};
|
||||
use tokio::sync::watch;
|
||||
|
||||
pub(crate) struct OldProcess {
|
||||
@@ -29,7 +29,11 @@ impl Host {
|
||||
let processes = state
|
||||
.processes
|
||||
.iter()
|
||||
.filter(|(_, process)| process.info.plugin == plugin && process.info.version != current)
|
||||
.filter(|(_, process)| {
|
||||
process.info.plugin == plugin
|
||||
&& process.info.version != current
|
||||
&& process.info.method != "rpc.discover"
|
||||
})
|
||||
.map(|(pid, process)| OldProcess {
|
||||
pid: pid.clone(),
|
||||
version: process.info.version.clone(),
|
||||
@@ -44,12 +48,17 @@ impl Host {
|
||||
.split_once('.')
|
||||
.is_some_and(|(name, _)| name == plugin)
|
||||
{
|
||||
let ended = state
|
||||
let process = state
|
||||
.processes
|
||||
.values()
|
||||
.find(|process| process.id.as_ref() == Some(&service.request))
|
||||
.map(|process| process.ended.subscribe());
|
||||
services.push((service, ended));
|
||||
.find(|process| process.id.as_ref() == Some(&service.request));
|
||||
if process.is_some_and(|process| process.info.version == current)
|
||||
|| (process.is_none() && service.starting.load(Ordering::SeqCst))
|
||||
{
|
||||
retained.push(service);
|
||||
} else {
|
||||
services.push((service, process.map(|process| process.ended.subscribe())));
|
||||
}
|
||||
} else {
|
||||
retained.push(service);
|
||||
}
|
||||
@@ -67,6 +76,9 @@ impl Host {
|
||||
pub(crate) fn begin_upgrade(&self, upgrade: Upgrade) {
|
||||
let host = self.clone();
|
||||
tokio::spawn(async move {
|
||||
if host.0.shutdown.load(Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
host.0.journal.write(
|
||||
"core",
|
||||
&upgrade.plugin,
|
||||
@@ -96,7 +108,9 @@ impl Host {
|
||||
if let Some(mut ended) = ended {
|
||||
let _ = ended.wait_for(|ended| *ended).await;
|
||||
}
|
||||
host.start_service(service.method, service.params).await;
|
||||
if !host.0.shutdown.load(Ordering::SeqCst) {
|
||||
host.start_service(service.method, service.params).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (version, processes) in versions {
|
||||
@@ -116,6 +130,32 @@ impl Host {
|
||||
for mut ended in processes {
|
||||
let _ = ended.wait_for(|ended| *ended).await;
|
||||
}
|
||||
loop {
|
||||
if host.0.shutdown.load(Ordering::SeqCst) {
|
||||
host.0.retirements.lock().unwrap().remove(&retirement);
|
||||
return;
|
||||
}
|
||||
let live: Vec<_> = host
|
||||
.0
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.processes
|
||||
.values()
|
||||
.filter(|process| {
|
||||
process.info.plugin == plugin
|
||||
&& process.info.version == version
|
||||
&& process.info.method != "rpc.discover"
|
||||
})
|
||||
.map(|process| process.ended.subscribe())
|
||||
.collect();
|
||||
if live.is_empty() {
|
||||
break;
|
||||
}
|
||||
for mut ended in live {
|
||||
let _ = ended.wait_for(|ended| *ended).await;
|
||||
}
|
||||
}
|
||||
if host
|
||||
.0
|
||||
.graph
|
||||
|
||||
Reference in New Issue
Block a user