use std::sync::Arc;
use nodedb_cluster::rpc_codec::{DescriptorVersionEntry, ExecuteRequest, ShuffleProduceResponse};
use nodedb_cluster::{ShuffleProduceRequest, TypedClusterError};
use nodedb_cluster::PlanExecutor;
use super::fanout::{ShuffleFanoutSink, ShuffleFanoutSinkParams};
use crate::control::LocalPlanExecutor;
use crate::control::state::SharedState;
pub struct RegistryShuffleProducer {
state: Arc<SharedState>,
}
impl RegistryShuffleProducer {
pub fn new(state: Arc<SharedState>) -> Self {
Self { state }
}
async fn produce(&self, req: ShuffleProduceRequest) -> ShuffleProduceResponse {
let Some(transport) = self.state.cluster_transport.clone() else {
return ShuffleProduceResponse {
error: Some(TypedClusterError::Internal {
code: 0,
message: "shuffle produce requires a cluster transport (single-node mode)"
.into(),
}),
read_version_lsn: 0,
};
};
let part_node_map: Vec<(u32, u64)> = req
.part_node_map
.iter()
.map(|e| (e.part, e.node_id))
.collect();
let mut sink = ShuffleFanoutSink::new(
transport,
Arc::clone(&self.state.shuffle_registry),
ShuffleFanoutSinkParams {
self_node_id: self.state.node_id,
shuffle_id: req.shuffle_id,
side: req.side,
num_parts: req.num_parts,
producer_count: req.producer_count,
keys: req.keys.clone(),
part_node_map: &part_node_map,
},
);
let exec_req = ExecuteRequest {
plan_bytes: req.plan_bytes,
tenant_id: req.tenant_id,
database_id: req.database_id,
deadline_remaining_ms: req.deadline_remaining_ms,
trace_id: req.trace_id,
descriptor_versions: req
.descriptor_versions
.iter()
.map(|d| DescriptorVersionEntry {
collection: d.collection.clone(),
version: d.version,
})
.collect(),
txn_id: None,
};
let executor = LocalPlanExecutor::new(Arc::clone(&self.state));
let scan_outcome = executor.execute_plan_streaming(exec_req, &mut sink).await;
let observed_read_version = sink.observed_read_version_lsn();
if let Err(e) = sink.finish(scan_outcome.clone()).await {
return ShuffleProduceResponse {
error: Some(scan_outcome.unwrap_or(TypedClusterError::Internal {
code: 0,
message: format!("shuffle produce fan-out finalize failed: {e}"),
})),
read_version_lsn: 0,
};
}
let read_version_lsn = if scan_outcome.is_none() {
observed_read_version
} else {
0
};
ShuffleProduceResponse {
error: scan_outcome,
read_version_lsn,
}
}
}
#[async_trait::async_trait]
impl nodedb_cluster::ShuffleProducer for RegistryShuffleProducer {
async fn on_shuffle_produce(&self, req: ShuffleProduceRequest) -> ShuffleProduceResponse {
self.produce(req).await
}
}