use futures::future::join_all;
use crate::bridge::envelope::{Payload, PhysicalPlan};
use crate::control::gateway::version_set::GatewayVersionSet;
use crate::control::server::graph_dispatch::cluster_resolve::{
DispatchSuperstepParams, dispatch_superstep_to_node, gateway_shared,
};
use crate::types::{DatabaseId, TenantId};
use nodedb_graph::{AlgoParams, GraphAlgorithm};
use nodedb_physical::physical_plan::{BspSuperstepPlan, BspSuperstepResult, GraphOp};
pub(super) struct ShardDispatch {
pub(super) node_id: u64,
pub(super) is_local: bool,
pub(super) owned_vshards: Vec<u32>,
pub(super) route_vshard: u32,
pub(super) incoming_contributions: Vec<(String, f64)>,
pub(super) rank_seed: Vec<(String, f64)>,
pub(super) global_dangling: f64,
pub(super) personalization_sum: f64,
}
pub(super) struct ShardResult {
pub(super) node_id: u64,
pub(super) result: BspSuperstepResult,
}
pub(super) struct ScatterSuperstepParams<'a> {
pub(super) tenant_id: TenantId,
pub(super) database_id: DatabaseId,
pub(super) algorithm: GraphAlgorithm,
pub(super) params: &'a AlgoParams,
pub(super) superstep: u32,
pub(super) global_n: usize,
pub(super) dispatches: Vec<ShardDispatch>,
pub(super) deadline_ms: u64,
}
pub(super) async fn scatter_superstep(
state: &crate::control::state::SharedState,
args: ScatterSuperstepParams<'_>,
) -> crate::Result<Vec<ShardResult>> {
let ScatterSuperstepParams {
tenant_id,
database_id,
algorithm,
params,
superstep,
global_n,
dispatches,
deadline_ms,
} = args;
let shared_arc = gateway_shared(state)?;
let version_set = GatewayVersionSet::from_pairs(Vec::new());
let futs = dispatches.into_iter().map(|d| {
let plan = PhysicalPlan::Graph(GraphOp::BspSuperstep(Box::new(BspSuperstepPlan {
algorithm,
params: params.clone(),
superstep,
global_n,
owned_vshards: d.owned_vshards.clone(),
incoming_contributions: d.incoming_contributions,
rank_seed: d.rank_seed,
global_dangling: d.global_dangling,
personalization_sum: d.personalization_sum,
})));
let version_set = version_set.clone();
let node_id = d.node_id;
let is_local = d.is_local;
let route_vshard = d.route_vshard;
let shared_arc = shared_arc.clone();
Box::pin(async move {
let payload = dispatch_superstep_to_node(
&shared_arc,
DispatchSuperstepParams {
tenant_id,
database_id,
deadline_ms,
node_id,
is_local,
route_vshard,
plan,
version_set: &version_set,
},
)
.await?;
let result = decode_single_result_from_payload(node_id, payload)?;
Ok::<ShardResult, crate::Error>(ShardResult { node_id, result })
})
});
let results = join_all(futs).await;
let mut out = Vec::with_capacity(results.len());
for res in results {
out.push(res?);
}
Ok(out)
}
fn decode_single_result_from_payload(
node_id: u64,
payload: Payload,
) -> crate::Result<BspSuperstepResult> {
if payload.is_empty() {
return Ok(BspSuperstepResult::default());
}
zerompk::from_msgpack::<BspSuperstepResult>(payload.as_ref()).map_err(|e| crate::Error::Codec {
detail: format!("bsp pagerank: node={node_id} result decode: {e}"),
})
}