This repository has been archived on 2026-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
el-retired/engram/crates/engram-server/src/routes/swarm.rs
T

125 lines
4.2 KiB
Rust

/// Swarm routes — distributed activation across the peer network.
///
/// POST /swarm/activate — SwarmActivateRequest → SwarmActivateResponse
/// GET /swarm/status — peer health check and last sync times
use axum::{
extract::State,
http::StatusCode,
Json,
};
use engram_sync::{
client::SyncClient, merge_activation_results, Peer, PeerActivationResult, PeerStatus,
SerializableActivatedNode, SwarmActivateRequest, SwarmActivateResponse,
};
use std::sync::Arc;
use uuid::Uuid;
use crate::state::AppState;
/// POST /swarm/activate
///
/// Runs spreading activation locally, then (if include_peers=true) fans out
/// to all trusted peers in parallel and returns merged results.
pub async fn swarm_activate(
State(state): State<Arc<AppState>>,
Json(req): Json<SwarmActivateRequest>,
) -> Result<Json<SwarmActivateResponse>, StatusCode> {
// Step 1: Run local activation. Lock db, compute, drop immediately.
let local_results: Vec<SerializableActivatedNode> = {
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let activated = db
.activate(&req.seeds, &req.query_embedding, req.max_depth, req.limit)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
activated.into_iter().map(Into::into).collect()
// db lock released here
};
// Step 2: Snapshot peer list (lock, read, drop).
let (our_id, trusted_peers): (Uuid, Vec<Peer>) = {
let engine = state.sync_engine.lock().await;
let id = engine.our_id();
let peers = engine.list_peers().iter().filter(|p| p.trusted).cloned().collect();
(id, peers)
// engine lock released here
};
// Step 3: Fan out to peers — no locks held across these awaits.
let mut peer_results: Vec<PeerActivationResult> = Vec::new();
if req.include_peers {
let mut handles = Vec::new();
for peer in trusted_peers {
let seeds = req.seeds.clone();
let embedding = req.query_embedding.clone();
let max_depth = req.max_depth;
let limit = req.limit;
handles.push(tokio::spawn(async move {
let client = SyncClient::new(peer.clone(), our_id);
match client.remote_activate(&seeds, &embedding, max_depth, limit).await {
Ok(results) => PeerActivationResult {
peer_id: peer.id,
peer_name: peer.name,
results,
error: None,
},
Err(e) => PeerActivationResult {
peer_id: peer.id,
peer_name: peer.name,
results: Vec::new(),
error: Some(e.to_string()),
},
}
}));
}
for handle in handles {
if let Ok(result) = handle.await {
peer_results.push(result);
}
}
}
// Step 4: Merge and return.
let merged = merge_activation_results(&local_results, &peer_results, req.limit);
Ok(Json(SwarmActivateResponse {
local_results,
peer_results,
merged,
}))
}
/// GET /swarm/status
///
/// Returns peer list with reachability status and last sync times.
pub async fn swarm_status(
State(state): State<Arc<AppState>>,
) -> Result<Json<Vec<PeerStatus>>, StatusCode> {
// Snapshot peer list and our_id without holding the lock across awaits.
let (our_id, peers): (Uuid, Vec<Peer>) = {
let engine = state.sync_engine.lock().await;
let id = engine.our_id();
let peers = engine.list_peers().to_vec();
(id, peers)
};
let mut statuses: Vec<PeerStatus> = Vec::new();
for peer in peers {
use engram_core::types::now_ms;
let client = SyncClient::new(peer.clone(), our_id);
let reachable = client.pull_delta(now_ms()).await.is_ok();
statuses.push(PeerStatus {
peer_id: peer.id,
peer_name: peer.name,
address: peer.address,
last_sync_at: peer.last_sync_at,
reachable,
sync_tiers: peer.sync_tiers,
trusted: peer.trusted,
});
}
Ok(Json(statuses))
}