use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::sync::mpsc::{self, Sender};
use nodedb_cluster::calvin::{
CalvinCompletionRegistry, SEQUENCER_GROUP_ID, SequencerStateMachine, TxnId,
};
use crate::control::cluster::array_executor::DataPlaneArrayExecutor;
use crate::control::cluster::calvin::ReadResultEvent;
use crate::control::cluster::handle::ClusterHandle;
use crate::control::cluster::metadata_applier::MetadataCommitApplier;
use crate::control::cluster::spsc_applier::SpscCommitApplier;
use crate::control::cluster::start_raft_helpers::build_vshard_handler;
use crate::control::distributed_applier::{ApplyBatch, ProposeTracker, create_distributed_applier};
use crate::control::state::SharedState;
pub(super) struct GroupSetup {
pub(super) tracker: Arc<ProposeTracker>,
pub(super) data_applier: SpscCommitApplier,
pub(super) apply_rx: mpsc::Receiver<ApplyBatch>,
pub(super) calvin_completion_registry: Arc<CalvinCompletionRegistry>,
pub(super) calvin_verdict_rx: mpsc::Receiver<(TxnId, bool)>,
pub(super) sequencer_state_machine: Arc<Mutex<SequencerStateMachine>>,
pub(super) calvin_read_result_senders: Arc<Mutex<BTreeMap<u32, Sender<ReadResultEvent>>>>,
pub(super) metadata_applier: Arc<dyn nodedb_cluster::MetadataApplier>,
pub(super) plan_executor: Arc<crate::control::LocalPlanExecutor>,
pub(super) vshard_handler: nodedb_cluster::VShardEnvelopeHandler,
pub(super) tick_interval: Duration,
pub(super) snapshot_chunk_bytes: u64,
pub(super) orphan_partial_max_age_secs: u64,
pub(super) replication_factor: u32,
}
pub(super) fn build_group_setup(
handle: &ClusterHandle,
shared: &Arc<SharedState>,
data_dir: &std::path::Path,
transport_tuning: &nodedb_types::config::tuning::ClusterTransportTuning,
) -> crate::Result<(nodedb_cluster::multi_raft::MultiRaft, GroupSetup)> {
let mut multi_raft = handle
.multi_raft
.lock()
.unwrap_or_else(|p| p.into_inner())
.take()
.ok_or_else(|| crate::Error::Config {
detail: "start_raft called twice: cluster multi_raft already consumed".into(),
})?;
if !multi_raft.contains_group(SEQUENCER_GROUP_ID) {
let sequencer_peers: Vec<u64> = {
let topo = handle.topology.read().unwrap_or_else(|p| p.into_inner());
topo.all_nodes()
.filter(|node| node.node_id != handle.node_id && node.state.receives_log())
.map(|node| node.node_id)
.collect()
};
multi_raft
.add_group(SEQUENCER_GROUP_ID, sequencer_peers)
.map_err(|e| crate::Error::Config {
detail: format!("sequencer raft group add: {e}"),
})?;
}
let tracker =
Arc::new(ProposeTracker::new().with_group_watchers(handle.group_watchers.clone()));
let (dist_applier, apply_rx) = create_distributed_applier(tracker.clone());
let dist_applier = Arc::new(dist_applier);
let (calvin_verdict_tx, calvin_verdict_rx) = mpsc::channel(512);
let calvin_completion_registry = CalvinCompletionRegistry::new(calvin_verdict_tx);
let sequencer_state_machine = Arc::new(Mutex::new(SequencerStateMachine::new(
std::collections::HashMap::new(),
Arc::clone(&calvin_completion_registry),
)));
let calvin_read_result_senders =
Arc::new(Mutex::new(BTreeMap::<u32, Sender<ReadResultEvent>>::new()));
if shared.propose_tracker.set(tracker.clone()).is_err() {
tracing::warn!("propose_tracker already set — start_raft appears to have run twice");
}
let data_applier = SpscCommitApplier::new(
shared.clone(),
dist_applier,
Arc::clone(&sequencer_state_machine),
);
let metadata_applier_concrete = Arc::new(MetadataCommitApplier::new(
handle.metadata_cache.clone(),
shared.catalog_change_tx.clone(),
shared.credentials.clone(),
));
metadata_applier_concrete.install_shared(Arc::downgrade(shared));
let metadata_applier: Arc<dyn nodedb_cluster::MetadataApplier> =
metadata_applier_concrete.clone();
let plan_executor = Arc::new(crate::control::LocalPlanExecutor::new(shared.clone()));
let array_executor: Arc<dyn nodedb_cluster::distributed_array::ArrayLocalExecutor> =
Arc::new(DataPlaneArrayExecutor::new(shared.clone()));
let cross_shard_receiver = Arc::new(crate::event::cross_shard::CrossShardReceiver::new(
Arc::new(crate::event::cross_shard::HwmStore::open(data_dir)?),
Arc::clone(shared),
Arc::new(crate::event::cross_shard::CrossShardMetrics::new()),
handle.node_id,
));
let vshard_handler = build_vshard_handler(array_executor, cross_shard_receiver);
let tick_interval = Duration::from_millis(transport_tuning.raft_tick_interval_ms);
let (snapshot_chunk_bytes, orphan_partial_max_age_secs) = {
let guard = handle
.pending_subsystems
.lock()
.unwrap_or_else(|p| p.into_inner());
let cfg = guard.as_ref().ok_or_else(|| crate::Error::Config {
detail: "start_raft called twice: pending_subsystems already consumed".into(),
})?;
(
cfg.config.install_snapshot_chunk_bytes,
cfg.config.orphan_partial_max_age_secs,
)
};
let replication_factor = match handle.catalog.load_cluster_settings().map_err(|e| {
crate::Error::Config {
detail: format!("start_raft: failed to load cluster settings: {e}"),
}
})? {
Some(s) => s.replication_factor,
None => {
handle
.pending_subsystems
.lock()
.unwrap_or_else(|p| p.into_inner())
.as_ref()
.map(|p| p.config.replication_factor as u32)
.ok_or_else(|| crate::Error::Config {
detail: "start_raft: no replication factor available (catalog and config both absent)".to_string(),
})?
}
};
let setup = GroupSetup {
tracker,
data_applier,
apply_rx,
calvin_completion_registry,
calvin_verdict_rx,
sequencer_state_machine,
calvin_read_result_senders,
metadata_applier,
plan_executor,
vshard_handler,
tick_interval,
snapshot_chunk_bytes,
orphan_partial_max_age_secs,
replication_factor,
};
Ok((multi_raft, setup))
}