feox_server/
server.rs

1use crate::{config::Config, error::Result, network::Connection};
2use feoxdb::FeoxStore;
3use std::net::TcpListener;
4use std::os::fd::{AsRawFd, RawFd};
5use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
6use std::sync::Arc;
7use std::thread;
8use tracing::{debug, error, info};
9
10/// High-performance Redis-compatible server
11pub struct Server {
12    config: Config,
13    store: Arc<FeoxStore>,
14    shutdown: AtomicBool,
15    active_connections: AtomicUsize,
16}
17
18impl Server {
19    /// Create a new server with the given configuration
20    pub fn new(config: Config) -> Result<Self> {
21        config.validate()?;
22
23        // Create a single shared FeoxStore instance
24        let store = if let Some(ref data_path) = config.data_path {
25            let mut builder = FeoxStore::builder()
26                .device_path(data_path.clone())
27                .max_memory(config.max_memory_per_shard.unwrap_or(1024 * 1024 * 1024))
28                .enable_ttl(config.enable_ttl);
29
30            // Set file size if configured
31            if let Some(file_size) = config.file_size {
32                builder = builder.file_size(file_size);
33            }
34
35            Arc::new(builder.build()?)
36        } else {
37            Arc::new(
38                FeoxStore::builder()
39                    .max_memory(config.max_memory_per_shard.unwrap_or(1024 * 1024 * 1024))
40                    .enable_ttl(config.enable_ttl)
41                    .build()?,
42            )
43        };
44
45        Ok(Self {
46            config,
47            store,
48            shutdown: AtomicBool::new(false),
49            active_connections: AtomicUsize::new(0),
50        })
51    }
52
53    /// Run the server, spawning worker threads
54    ///
55    /// This method blocks until the server is shut down.
56    pub fn run(self: Arc<Self>) -> Result<()> {
57        // Create TCP listener
58        let listener =
59            TcpListener::bind(format!("{}:{}", self.config.bind_addr, self.config.port))?;
60
61        listener.set_nonblocking(true)?;
62        let listener_fd = listener.as_raw_fd();
63
64        info!(
65            "Server listening on {}:{}",
66            self.config.bind_addr, self.config.port
67        );
68
69        // Spawn worker threads
70        let mut handles = Vec::new();
71
72        for thread_id in 0..self.config.threads {
73            let server = Arc::clone(&self);
74            let store = Arc::clone(&self.store);
75            let handle = thread::spawn(move || {
76                if let Err(e) = server.run_worker(thread_id, listener_fd, store) {
77                    error!("Worker {} failed: {}", thread_id, e);
78                }
79            });
80            handles.push(handle);
81        }
82
83        // Wait for all workers to finish
84        for handle in handles {
85            let _ = handle.join();
86        }
87
88        Ok(())
89    }
90
91    /// Signal the server to shut down gracefully
92    pub fn shutdown(&self) {
93        info!("Initiating server shutdown");
94        self.shutdown.store(true, Ordering::Release);
95    }
96
97    /// Get the number of active client connections
98    pub fn active_connections(&self) -> usize {
99        self.active_connections.load(Ordering::Acquire)
100    }
101
102    fn run_worker(
103        self: &Arc<Self>,
104        thread_id: usize,
105        listener_fd: RawFd,
106        store: Arc<FeoxStore>,
107    ) -> Result<()> {
108        use mio::net::{TcpListener as MioTcpListener, TcpStream as MioTcpStream};
109        use mio::{Events, Interest, Poll, Token};
110        use std::collections::HashMap;
111        use std::io::{ErrorKind, Read, Write};
112        use std::os::fd::FromRawFd;
113
114        // Create mio Poll instance
115        let mut poll = Poll::new()?;
116        let mut events = Events::with_capacity(1024);
117
118        // Convert raw fd to mio listener
119        let std_listener = unsafe { TcpListener::from_raw_fd(listener_fd) };
120        std_listener.set_nonblocking(true)?;
121        let mut listener = MioTcpListener::from_std(std_listener);
122
123        // Register listener
124        const SERVER: Token = Token(0);
125        poll.registry()
126            .register(&mut listener, SERVER, Interest::READABLE)?;
127
128        // Connection tracking
129        let mut connections: HashMap<Token, (MioTcpStream, Connection)> = HashMap::new();
130        let mut next_token = 1usize;
131
132        info!("Worker {} started", thread_id);
133
134        // Event loop
135        while !self.shutdown.load(Ordering::Acquire) {
136            // Poll for events with 100ms timeout
137            poll.poll(&mut events, Some(std::time::Duration::from_millis(100)))?;
138
139            for event in events.iter() {
140                match event.token() {
141                    SERVER => {
142                        // Accept new connections
143                        loop {
144                            match listener.accept() {
145                                Ok((mut stream, addr)) => {
146                                    debug!("New connection from {:?}", addr);
147
148                                    // Configure socket
149                                    stream.set_nodelay(self.config.tcp_nodelay)?;
150
151                                    let token = Token(next_token);
152                                    next_token += 1;
153
154                                    // Register for read events
155                                    poll.registry().register(
156                                        &mut stream,
157                                        token,
158                                        Interest::READABLE,
159                                    )?;
160
161                                    let connection = Connection::new(
162                                        0, // fd not used in this path
163                                        self.config.connection_buffer_size,
164                                        Arc::clone(&store),
165                                    );
166
167                                    connections.insert(token, (stream, connection));
168                                    self.active_connections.fetch_add(1, Ordering::Relaxed);
169                                }
170                                Err(e) if e.kind() == ErrorKind::WouldBlock => break,
171                                Err(e) => {
172                                    error!("Error accepting connection: {}", e);
173                                    break;
174                                }
175                            }
176                        }
177                    }
178                    token => {
179                        // Handle client connection
180                        let should_close = if let Some((stream, connection)) =
181                            connections.get_mut(&token)
182                        {
183                            let mut should_close = false;
184
185                            if event.is_readable() {
186                                // Use a simple buffer (optimize with pool later if needed)
187                                let mut buffer = vec![0u8; 8192];
188
189                                match stream.read(&mut buffer) {
190                                    Ok(0) => {
191                                        // Connection closed
192                                        should_close = true;
193                                    }
194                                    Ok(n) => {
195                                        // Process commands inline
196                                        if let Err(e) = connection.process_read(&buffer[..n]) {
197                                            error!("Error processing read: {}", e);
198                                            should_close = true;
199                                        } else {
200                                            // Write response immediately
201                                            while let Some(response_data) =
202                                                connection.pending_writes()
203                                            {
204                                                let response_len = response_data.len();
205                                                match stream.write(response_data) {
206                                                    Ok(n) => {
207                                                        connection.consume_writes(n);
208                                                        if n < response_len {
209                                                            // Partial write, would block
210                                                            break;
211                                                        }
212                                                    }
213                                                    Err(e) if e.kind() == ErrorKind::WouldBlock => {
214                                                        break;
215                                                    }
216                                                    Err(e) => {
217                                                        error!("Error writing: {}", e);
218                                                        should_close = true;
219                                                        break;
220                                                    }
221                                                }
222                                            }
223                                        }
224
225                                        if connection.is_closed() {
226                                            should_close = true;
227                                        }
228                                    }
229                                    Err(e) if e.kind() != ErrorKind::WouldBlock => {
230                                        if e.kind() != ErrorKind::ConnectionReset {
231                                            error!("Error reading: {}", e);
232                                        }
233                                        should_close = true;
234                                    }
235                                    Err(_) => {} // WouldBlock - ignore
236                                }
237                            }
238
239                            should_close
240                        } else {
241                            false
242                        };
243
244                        if should_close {
245                            if let Some((mut stream, mut connection)) = connections.remove(&token) {
246                                let _ = poll.registry().deregister(&mut stream);
247                                connection.close();
248                                self.active_connections.fetch_sub(1, Ordering::Relaxed);
249                            }
250                        }
251                    }
252                }
253            }
254        }
255
256        // Cleanup
257        for (_, (mut stream, mut connection)) in connections {
258            let _ = poll.registry().deregister(&mut stream);
259            connection.close();
260        }
261
262        info!("Worker {} shutting down", thread_id);
263        Ok(())
264    }
265}