photoncloud-monorepo/chainfire/crates/chainfire-core/src/builder.rs
centra 8f94aee1fa Fix R8: Convert submodule gitlinks to regular directories
- 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>
2025-12-09 16:51:20 +09:00

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)
}
}