d_engine_server/api/
standalone.rs

1//! Standalone mode for d-engine - independent server deployment
2
3use std::sync::Arc;
4
5use tokio::sync::watch;
6
7use crate::Result;
8#[cfg(feature = "rocksdb")]
9use crate::RocksDBStateMachine;
10#[cfg(feature = "rocksdb")]
11use crate::RocksDBStorageEngine;
12use crate::StateMachine;
13use crate::StorageEngine;
14use crate::node::NodeBuilder;
15
16/// Standalone d-engine server for independent deployment
17pub struct StandaloneServer;
18
19impl StandaloneServer {
20    /// Run server with configuration from environment.
21    ///
22    /// Reads `CONFIG_PATH` environment variable or uses default configuration.
23    /// Data directory is determined by config's `cluster.db_root_dir` setting.
24    /// Blocks until shutdown signal is received.
25    ///
26    /// # Arguments
27    /// * `shutdown_rx` - Shutdown signal receiver
28    ///
29    /// # Example
30    /// ```ignore
31    /// // Set config path via environment variable
32    /// std::env::set_var("CONFIG_PATH", "/etc/d-engine/production.toml");
33    ///
34    /// let (shutdown_tx, shutdown_rx) = watch::channel(());
35    /// StandaloneServer::run(shutdown_rx).await?;
36    /// ```
37    #[cfg(feature = "rocksdb")]
38    pub async fn run(shutdown_rx: watch::Receiver<()>) -> Result<()> {
39        let config = d_engine_core::RaftNodeConfig::new()?.validate()?;
40        let base_dir = std::path::PathBuf::from(&config.cluster.db_root_dir);
41
42        tokio::fs::create_dir_all(&base_dir)
43            .await
44            .map_err(|e| crate::Error::Fatal(format!("Failed to create data directory: {e}")))?;
45
46        let storage_path = base_dir.join("storage");
47        let sm_path = base_dir.join("state_machine");
48
49        tracing::info!("Starting standalone server with RocksDB at {:?}", base_dir);
50
51        let storage = Arc::new(RocksDBStorageEngine::new(storage_path)?);
52        let mut sm = RocksDBStateMachine::new(sm_path)?;
53
54        // Inject lease if enabled
55        let lease_cfg = &config.raft.state_machine.lease;
56        if lease_cfg.enabled {
57            let lease = Arc::new(crate::storage::DefaultLease::new(lease_cfg.clone()));
58            sm.set_lease(lease);
59        }
60
61        let sm = Arc::new(sm);
62
63        Self::run_custom(storage, sm, shutdown_rx, None).await
64    }
65
66    /// Run server with explicit configuration file.
67    ///
68    /// Reads configuration from specified file path.
69    /// Data directory is determined by config's `cluster.db_root_dir` setting.
70    /// Blocks until shutdown signal is received.
71    ///
72    /// # Arguments
73    /// * `config_path` - Path to configuration file
74    /// * `shutdown_rx` - Shutdown signal receiver
75    ///
76    /// # Example
77    /// ```ignore
78    /// let (shutdown_tx, shutdown_rx) = watch::channel(());
79    /// StandaloneServer::run_with("config/node1.toml", shutdown_rx).await?;
80    /// ```
81    #[cfg(feature = "rocksdb")]
82    pub async fn run_with(
83        config_path: &str,
84        shutdown_rx: watch::Receiver<()>,
85    ) -> Result<()> {
86        let config = d_engine_core::RaftNodeConfig::new()?
87            .with_override_config(config_path)?
88            .validate()?;
89        let base_dir = std::path::PathBuf::from(&config.cluster.db_root_dir);
90
91        tokio::fs::create_dir_all(&base_dir)
92            .await
93            .map_err(|e| crate::Error::Fatal(format!("Failed to create data directory: {e}")))?;
94
95        let storage_path = base_dir.join("storage");
96        let sm_path = base_dir.join("state_machine");
97
98        tracing::info!("Starting standalone server with RocksDB at {:?}", base_dir);
99
100        let storage = Arc::new(RocksDBStorageEngine::new(storage_path)?);
101        let mut sm = RocksDBStateMachine::new(sm_path)?;
102
103        // Inject lease if enabled
104        let lease_cfg = &config.raft.state_machine.lease;
105        if lease_cfg.enabled {
106            let lease = Arc::new(crate::storage::DefaultLease::new(lease_cfg.clone()));
107            sm.set_lease(lease);
108        }
109
110        let sm = Arc::new(sm);
111
112        Self::run_custom(storage, sm, shutdown_rx, Some(config_path)).await
113    }
114
115    /// Run server with custom storage engine and state machine.
116    ///
117    /// Advanced API for users providing custom storage implementations.
118    /// Blocks until shutdown signal is received.
119    ///
120    /// # Arguments
121    /// * `config` - Node configuration
122    /// * `storage_engine` - Custom storage engine implementation
123    /// * `state_machine` - Custom state machine implementation
124    /// * `shutdown_rx` - Shutdown signal receiver
125    ///
126    /// # Example
127    /// ```ignore
128    /// let storage = Arc::new(MyCustomStorage::new()?);
129    /// let sm = Arc::new(MyCustomStateMachine::new()?);
130    ///
131    /// let (shutdown_tx, shutdown_rx) = watch::channel(());
132    /// StandaloneServer::run_custom(storage, sm, shutdown_rx, Some("config.toml")).await?;
133    /// ```
134    pub async fn run_custom<SE, SM>(
135        storage_engine: Arc<SE>,
136        state_machine: Arc<SM>,
137        shutdown_rx: watch::Receiver<()>,
138        config_path: Option<&str>,
139    ) -> Result<()>
140    where
141        SE: StorageEngine + std::fmt::Debug + 'static,
142        SM: StateMachine + std::fmt::Debug + 'static,
143    {
144        let node_config = if let Some(path) = config_path {
145            d_engine_core::RaftNodeConfig::default()
146                .with_override_config(path)?
147                .validate()?
148        } else {
149            d_engine_core::RaftNodeConfig::new()?.validate()?
150        };
151
152        let node = NodeBuilder::init(node_config, shutdown_rx)
153            .storage_engine(storage_engine)
154            .state_machine(state_machine)
155            .start()
156            .await?;
157
158        node.run().await
159    }
160}