feat(sidecar): Phase B-3 — SidecarPool consolida sidecars en un runtime
Antes: cada spawn(card) creaba un thread + tokio runtime propio.
Para módulos con muchas sesiones (nouser daemon con 50+ Mónadas)
eso es 50 threads + 50 runtimes. Ahora: un thread + un runtime
tokio current_thread que hostea N tasks de sidecar.
API nueva (aditiva, no rompe spawn/spawn_with_handle):
let pool = SidecarPool::new()?;
pool.spawn(card1);
pool.spawn(card2);
pool.spawn_conscious(card_with_wit, wit);
pool.spawn_with_config(custom_config);
// pool drop = todas las sesiones cierran.
run_client se hace pública para que el pool pueda enqueuar tasks
externos al runtime con handle.spawn(run_client(config)).
nouser daemon migrado al pool. Verificación con ps -L:
$ ps -L -p $(pidof nouser)
LWP CMD
28817 nouser # main thread
28819 brahman-sidecar # pool thread (todas las sesiones)
Antes serían 6+ LWP (1 main + N sesiones). Ahora 2 fijos sin
importar cuántas Mónadas se publiquen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,11 @@ fn cmd_json(args: &[String]) -> Cmd {
|
||||
fn cmd_daemon(args: &[String]) -> Cmd {
|
||||
let dir = require_dir(args)?;
|
||||
|
||||
// Pool consolidado: 1 thread + 1 tokio runtime para TODAS las
|
||||
// sesiones (engine + N mónadas). Antes era 1 thread por sesión.
|
||||
let pool = brahman_sidecar::SidecarPool::new()
|
||||
.map_err(|e| format!("crear pool: {e}"))?;
|
||||
|
||||
// 1. El propio engine se presenta como Ente.
|
||||
let engine_card = build_engine_card();
|
||||
let engine_id = engine_card.id;
|
||||
@@ -172,7 +177,7 @@ fn cmd_daemon(args: &[String]) -> Cmd {
|
||||
"nouser daemon: publicando engine '{}' (kind=Ente, id={})",
|
||||
engine_label, engine_id
|
||||
);
|
||||
brahman_sidecar::spawn(engine_card);
|
||||
pool.spawn(engine_card);
|
||||
|
||||
// 2. Scan y cluster.
|
||||
let (db, n_files) = run_scan(&dir)?;
|
||||
@@ -184,10 +189,9 @@ fn cmd_daemon(args: &[String]) -> Cmd {
|
||||
);
|
||||
|
||||
// 3. Cada Mónada se presenta como Card de tipo Data, declarando
|
||||
// su relación OwnedBy con el engine. La UI puede entonces
|
||||
// cruzar referencias para reconstruir el grafo
|
||||
// "nouser_engine posee Mónada X" sin lookup adicional.
|
||||
let mut handles = Vec::with_capacity(db.monad_count());
|
||||
// su relación OwnedBy con el engine. Todas comparten el runtime
|
||||
// del pool — sin overhead de N threads.
|
||||
let mut count = 0usize;
|
||||
for monad in db.monads() {
|
||||
let mut card = monad.to_brahman_card();
|
||||
card.references.push(brahman_card::CardReference {
|
||||
@@ -195,23 +199,17 @@ fn cmd_daemon(args: &[String]) -> Cmd {
|
||||
target_id: engine_id,
|
||||
target_label: engine_label.clone(),
|
||||
});
|
||||
match brahman_sidecar::spawn_with_handle(brahman_sidecar::SidecarConfig::new(card)) {
|
||||
Ok(h) => handles.push(h),
|
||||
Err(e) => eprintln!(
|
||||
"nouser daemon: falló sidecar para mónada '{}': {e}",
|
||||
monad.label
|
||||
),
|
||||
}
|
||||
pool.spawn(card);
|
||||
count += 1;
|
||||
}
|
||||
eprintln!(
|
||||
"nouser daemon: {} sidecars activos (1 ente + {} data). Ctrl-C para terminar.",
|
||||
handles.len() + 1,
|
||||
handles.len()
|
||||
"nouser daemon: 1 ente + {} mónadas en pool consolidado. Ctrl-C para terminar.",
|
||||
count
|
||||
);
|
||||
|
||||
// 4. Park: el proceso principal queda dormido, los sidecars siguen
|
||||
// pingueando en sus threads.
|
||||
// 4. Park: el thread del pool sigue vivo mientras `pool` exista.
|
||||
std::thread::park();
|
||||
drop(pool);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user