- Remove gitlinks (160000 mode) for chainfire, flaredb, iam - Add workspace contents as regular tracked files - Update flake.nix to use simple paths instead of builtins.fetchGit This resolves the nix build failure where submodule directories appeared empty in the nix store. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
221 lines
6.3 KiB
Rust
221 lines
6.3 KiB
Rust
//! Builder pattern for cluster creation
|
|
|
|
use std::net::SocketAddr;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use chainfire_types::node::NodeRole;
|
|
use chainfire_types::RaftRole;
|
|
|
|
use crate::callbacks::{ClusterEventHandler, KvEventHandler};
|
|
use crate::cluster::Cluster;
|
|
use crate::config::{ClusterConfig, MemberConfig, StorageBackendConfig, TimeoutConfig};
|
|
use crate::error::{ClusterError, Result};
|
|
use crate::events::EventDispatcher;
|
|
|
|
/// Builder for creating a Chainfire cluster instance
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```ignore
|
|
/// use chainfire_core::ClusterBuilder;
|
|
///
|
|
/// let cluster = ClusterBuilder::new(1)
|
|
/// .name("node-1")
|
|
/// .gossip_addr("0.0.0.0:7946".parse()?)
|
|
/// .raft_addr("0.0.0.0:2380".parse()?)
|
|
/// .bootstrap(true)
|
|
/// .build()
|
|
/// .await?;
|
|
/// ```
|
|
pub struct ClusterBuilder {
|
|
config: ClusterConfig,
|
|
cluster_handlers: Vec<Arc<dyn ClusterEventHandler>>,
|
|
kv_handlers: Vec<Arc<dyn KvEventHandler>>,
|
|
}
|
|
|
|
impl ClusterBuilder {
|
|
/// Create a new cluster builder with the given node ID
|
|
pub fn new(node_id: u64) -> Self {
|
|
Self {
|
|
config: ClusterConfig {
|
|
node_id,
|
|
..Default::default()
|
|
},
|
|
cluster_handlers: Vec::new(),
|
|
kv_handlers: Vec::new(),
|
|
}
|
|
}
|
|
|
|
/// Set the node name
|
|
pub fn name(mut self, name: impl Into<String>) -> Self {
|
|
self.config.node_name = name.into();
|
|
self
|
|
}
|
|
|
|
/// Set the node role (ControlPlane or Worker)
|
|
pub fn role(mut self, role: NodeRole) -> Self {
|
|
self.config.node_role = role;
|
|
self
|
|
}
|
|
|
|
/// Set the Raft participation role (Voter, Learner, or None)
|
|
pub fn raft_role(mut self, role: RaftRole) -> Self {
|
|
self.config.raft_role = role;
|
|
self
|
|
}
|
|
|
|
/// Set the API listen address
|
|
pub fn api_addr(mut self, addr: SocketAddr) -> Self {
|
|
self.config.api_addr = Some(addr);
|
|
self
|
|
}
|
|
|
|
/// Set the Raft listen address (for control plane nodes)
|
|
pub fn raft_addr(mut self, addr: SocketAddr) -> Self {
|
|
self.config.raft_addr = Some(addr);
|
|
self
|
|
}
|
|
|
|
/// Set the gossip listen address
|
|
pub fn gossip_addr(mut self, addr: SocketAddr) -> Self {
|
|
self.config.gossip_addr = addr;
|
|
self
|
|
}
|
|
|
|
/// Set the storage backend
|
|
pub fn storage(mut self, backend: StorageBackendConfig) -> Self {
|
|
self.config.storage = backend;
|
|
self
|
|
}
|
|
|
|
/// Set the data directory (convenience method for RocksDB storage)
|
|
pub fn data_dir(mut self, path: impl Into<PathBuf>) -> Self {
|
|
self.config.storage = StorageBackendConfig::RocksDb { path: path.into() };
|
|
self
|
|
}
|
|
|
|
/// Use in-memory storage
|
|
pub fn memory_storage(mut self) -> Self {
|
|
self.config.storage = StorageBackendConfig::Memory;
|
|
self
|
|
}
|
|
|
|
/// Add initial cluster members (for bootstrap)
|
|
pub fn initial_members(mut self, members: Vec<MemberConfig>) -> Self {
|
|
self.config.initial_members = members;
|
|
self
|
|
}
|
|
|
|
/// Add a single initial member
|
|
pub fn add_member(mut self, member: MemberConfig) -> Self {
|
|
self.config.initial_members.push(member);
|
|
self
|
|
}
|
|
|
|
/// Enable cluster bootstrap (first node)
|
|
pub fn bootstrap(mut self, bootstrap: bool) -> Self {
|
|
self.config.bootstrap = bootstrap;
|
|
self
|
|
}
|
|
|
|
/// Set the cluster ID
|
|
pub fn cluster_id(mut self, id: u64) -> Self {
|
|
self.config.cluster_id = id;
|
|
self
|
|
}
|
|
|
|
/// Enable gRPC API server
|
|
pub fn with_grpc_api(mut self, enabled: bool) -> Self {
|
|
self.config.enable_grpc_api = enabled;
|
|
self
|
|
}
|
|
|
|
/// Set timeout configuration
|
|
pub fn timeouts(mut self, timeouts: TimeoutConfig) -> Self {
|
|
self.config.timeouts = timeouts;
|
|
self
|
|
}
|
|
|
|
/// Register a cluster event handler
|
|
///
|
|
/// Multiple handlers can be registered. They will all be called
|
|
/// when cluster events occur.
|
|
pub fn on_cluster_event<H>(mut self, handler: H) -> Self
|
|
where
|
|
H: ClusterEventHandler + 'static,
|
|
{
|
|
self.cluster_handlers.push(Arc::new(handler));
|
|
self
|
|
}
|
|
|
|
/// Register a cluster event handler (Arc version)
|
|
pub fn on_cluster_event_arc(mut self, handler: Arc<dyn ClusterEventHandler>) -> Self {
|
|
self.cluster_handlers.push(handler);
|
|
self
|
|
}
|
|
|
|
/// Register a KV event handler
|
|
///
|
|
/// Multiple handlers can be registered. They will all be called
|
|
/// when KV events occur.
|
|
pub fn on_kv_event<H>(mut self, handler: H) -> Self
|
|
where
|
|
H: KvEventHandler + 'static,
|
|
{
|
|
self.kv_handlers.push(Arc::new(handler));
|
|
self
|
|
}
|
|
|
|
/// Register a KV event handler (Arc version)
|
|
pub fn on_kv_event_arc(mut self, handler: Arc<dyn KvEventHandler>) -> Self {
|
|
self.kv_handlers.push(handler);
|
|
self
|
|
}
|
|
|
|
/// Validate the configuration
|
|
fn validate(&self) -> Result<()> {
|
|
if self.config.node_id == 0 {
|
|
return Err(ClusterError::Config("node_id must be non-zero".into()));
|
|
}
|
|
|
|
if self.config.node_name.is_empty() {
|
|
return Err(ClusterError::Config("node_name is required".into()));
|
|
}
|
|
|
|
// Raft-participating nodes need a Raft address
|
|
if self.config.raft_role.participates_in_raft() && self.config.raft_addr.is_none() {
|
|
return Err(ClusterError::Config(
|
|
"raft_addr is required for Raft-participating nodes".into(),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Build the cluster instance
|
|
///
|
|
/// This initializes the storage backend, Raft (if applicable), and gossip.
|
|
pub async fn build(self) -> Result<Cluster> {
|
|
self.validate()?;
|
|
|
|
// Create event dispatcher with registered handlers
|
|
let mut event_dispatcher = EventDispatcher::new();
|
|
for handler in self.cluster_handlers {
|
|
event_dispatcher.add_cluster_handler(handler);
|
|
}
|
|
for handler in self.kv_handlers {
|
|
event_dispatcher.add_kv_handler(handler);
|
|
}
|
|
|
|
// Create the cluster
|
|
let cluster = Cluster::new(self.config, event_dispatcher);
|
|
|
|
// TODO: Initialize storage backend
|
|
// TODO: Initialize Raft if role participates
|
|
// TODO: Initialize gossip
|
|
// TODO: Start background tasks
|
|
|
|
Ok(cluster)
|
|
}
|
|
}
|