feat(shipote): collision detection + stats history server-side (fase O)
- Flow socket names usan pipeline_id full (ULID 26 chars) + edge_idx. Cero colisiones entre pipelines (ULID es único global). Fallback con suffix -N si el path existe (cap 1000 retries). - WorkspaceState.stats_history (VecDeque cap 64) — workspace_stats appendea cada call. API workspace_stats_history(id, tail). Protocol WorkspaceStatsHistory. Shell pide history al primer probe → sparkline hidratada al boot, sobrevive restart del shell. 84 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 25, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -197,20 +197,31 @@ pub async fn run_pipeline(
|
||||
for s in &splitter_specs {
|
||||
let mut senders_per_edge = Vec::with_capacity(s.edges.len());
|
||||
let mut paths_per_edge = Vec::with_capacity(s.edges.len());
|
||||
for (i, em) in s.edges.iter().enumerate() {
|
||||
for (i, _em) in s.edges.iter().enumerate() {
|
||||
if !s.tap {
|
||||
senders_per_edge.push(None);
|
||||
paths_per_edge.push(None);
|
||||
continue;
|
||||
}
|
||||
let id = format!(
|
||||
"{}-{}-{}-{}",
|
||||
short_ulid(&pipeline_id),
|
||||
em.from_label,
|
||||
em.from_output,
|
||||
i
|
||||
);
|
||||
let socket = crate::flow_channel::default_flow_socket_path(&id);
|
||||
// Socket name = pipeline_id full (26 chars ULID) + edge_idx.
|
||||
// ULID es único globalmente → cero colisiones entre runs.
|
||||
// Edge_idx desambigua múltiples sockets del mismo pipeline.
|
||||
// No incluimos from_label en el name (puede tener chars que
|
||||
// no van en paths Unix — los hints van en `EdgeDiscernment`).
|
||||
let id = format!("{}-{}", pipeline_id, i);
|
||||
let mut socket = crate::flow_channel::default_flow_socket_path(&id);
|
||||
// Fallback: si el path existe (raro — daemon crashed sin
|
||||
// cleanup), agregar suffix numérico hasta encontrar libre.
|
||||
let mut suffix = 1u32;
|
||||
while socket.exists() {
|
||||
let alt = format!("{id}-{suffix}");
|
||||
socket = crate::flow_channel::default_flow_socket_path(&alt);
|
||||
suffix += 1;
|
||||
if suffix > 1000 {
|
||||
warn!(orig = id, "flow socket collision: 1000 retries — using as-is");
|
||||
break;
|
||||
}
|
||||
}
|
||||
match crate::flow_channel::FlowChannel::with_replay_caps(
|
||||
socket.clone(),
|
||||
crate::flow_channel::ReplayCaps::new(spec.discern.replay_chunks, spec.discern.replay_bytes),
|
||||
@@ -277,6 +288,7 @@ pub async fn run_pipeline(
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn short_ulid(u: &Ulid) -> String {
|
||||
let s = u.to_string();
|
||||
s[s.len() - 6..].to_string()
|
||||
|
||||
Reference in New Issue
Block a user