feox_server/server.rs
1use crate::client_registry::ClientRegistry;
2use crate::pubsub::{handle_pubsub_operation, GlobalRegistry, ThreadLocalPubSub};
3use crate::{config::Config, error::Result, network::Connection};
4use feoxdb::FeoxStore;
5use std::net::TcpListener;
6use std::os::fd::{AsRawFd, RawFd};
7use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
8use std::sync::Arc;
9use std::thread;
10use tracing::{debug, error, info};
11
12/// High-performance Redis-compatible server
13pub struct Server {
14 config: Config,
15 store: Arc<FeoxStore>,
16 shutdown: AtomicBool,
17 active_connections: AtomicUsize,
18 client_registry: Arc<ClientRegistry>,
19}
20
21impl Server {
22 /// Create a new server with the given configuration
23 pub fn new(config: Config) -> Result<Self> {
24 config.validate()?;
25
26 // Create a single shared FeoxStore instance
27 let store = if let Some(ref data_path) = config.data_path {
28 let mut builder = FeoxStore::builder()
29 .device_path(data_path.clone())
30 .max_memory(config.max_memory_per_shard.unwrap_or(1024 * 1024 * 1024))
31 .enable_ttl(config.enable_ttl);
32
33 // Set file size if configured
34 if let Some(file_size) = config.file_size {
35 builder = builder.file_size(file_size);
36 }
37
38 Arc::new(builder.build()?)
39 } else {
40 Arc::new(
41 FeoxStore::builder()
42 .max_memory(config.max_memory_per_shard.unwrap_or(1024 * 1024 * 1024))
43 .enable_ttl(config.enable_ttl)
44 .build()?,
45 )
46 };
47
48 let client_registry = Arc::new(ClientRegistry::new());
49
50 Ok(Self {
51 config,
52 store,
53 shutdown: AtomicBool::new(false),
54 active_connections: AtomicUsize::new(0),
55 client_registry,
56 })
57 }
58
59 /// Run the server, spawning worker threads
60 ///
61 /// This method blocks until the server is shut down.
62 pub fn run(self: Arc<Self>) -> Result<()> {
63 // Create TCP listener
64 let listener =
65 TcpListener::bind(format!("{}:{}", self.config.bind_addr, self.config.port))?;
66
67 listener.set_nonblocking(true)?;
68 let listener_fd = listener.as_raw_fd();
69
70 info!(
71 "Server listening on {}:{}",
72 self.config.bind_addr, self.config.port
73 );
74
75 // Create pub/sub registry and receivers
76 let (pubsub_registry, mut pubsub_receivers) = GlobalRegistry::new(self.config.threads);
77
78 // Spawn worker threads
79 let mut handles = Vec::new();
80
81 for thread_id in 0..self.config.threads {
82 let server = Arc::clone(&self);
83 let store = Arc::clone(&self.store);
84 let pubsub_registry = Arc::clone(&pubsub_registry);
85 let pubsub_receiver = pubsub_receivers.remove(0);
86 let client_registry = Arc::clone(&self.client_registry);
87
88 let handle = thread::spawn(move || {
89 if let Err(e) = server.run_worker(
90 thread_id,
91 listener_fd,
92 store,
93 pubsub_registry,
94 pubsub_receiver,
95 client_registry,
96 ) {
97 error!("Worker {} failed: {}", thread_id, e);
98 }
99 });
100 handles.push(handle);
101 }
102
103 // Wait for all workers to finish
104 for handle in handles {
105 let _ = handle.join();
106 }
107
108 Ok(())
109 }
110
111 /// Signal the server to shut down gracefully
112 pub fn shutdown(&self) {
113 info!("Initiating server shutdown");
114 self.shutdown.store(true, Ordering::Release);
115 }
116
117 /// Get the number of active client connections
118 pub fn active_connections(&self) -> usize {
119 self.active_connections.load(Ordering::Acquire)
120 }
121
122 fn run_worker(
123 self: &Arc<Self>,
124 thread_id: usize,
125 listener_fd: RawFd,
126 store: Arc<FeoxStore>,
127 pubsub_registry: Arc<GlobalRegistry>,
128 pubsub_receiver: crossbeam_channel::Receiver<crate::pubsub::BroadcastMsg>,
129 client_registry: Arc<ClientRegistry>,
130 ) -> Result<()> {
131 use mio::net::{TcpListener as MioTcpListener, TcpStream as MioTcpStream};
132 use mio::{Events, Interest, Poll, Token};
133 use std::collections::HashMap;
134 use std::io::{ErrorKind, Read, Write};
135 use std::os::fd::FromRawFd;
136
137 // Create mio Poll instance
138 let mut poll = Poll::new()?;
139 let mut events = Events::with_capacity(1024);
140
141 // Convert raw fd to mio listener
142 let std_listener = unsafe { TcpListener::from_raw_fd(listener_fd) };
143 std_listener.set_nonblocking(true)?;
144 let mut listener = MioTcpListener::from_std(std_listener);
145
146 // Register listener
147 const SERVER: Token = Token(0);
148 poll.registry()
149 .register(&mut listener, SERVER, Interest::READABLE)?;
150
151 // Connection tracking
152 let mut connections: HashMap<Token, (MioTcpStream, Connection)> = HashMap::new();
153 let mut next_token = 1usize;
154
155 // Initialize thread-local pub/sub
156 let mut pubsub_manager =
157 ThreadLocalPubSub::new(thread_id, pubsub_receiver, pubsub_registry.clone());
158
159 info!("Worker {} started", thread_id);
160
161 // Event loop
162 while !self.shutdown.load(Ordering::Acquire) {
163 // Process incoming pub/sub messages
164 let pubsub_deliveries = pubsub_manager.process_inbox();
165 for (conn_id, message) in pubsub_deliveries {
166 // Find connection by ID and queue message
167 for (_token, (stream, connection)) in connections.iter_mut() {
168 if connection.connection_id == conn_id {
169 connection.queue_pubsub_message(message);
170 connection.process_pubsub_messages();
171
172 // Write any pending data immediately
173 while let Some(data) = connection.pending_writes() {
174 let data_len = data.len();
175 match stream.write(data) {
176 Ok(n) => {
177 connection.consume_writes(n);
178 if n < data_len {
179 break;
180 }
181 }
182 Err(e) if e.kind() == ErrorKind::WouldBlock => break,
183 Err(_) => break,
184 }
185 }
186 break;
187 }
188 }
189 }
190
191 // Poll for events with 100ms timeout
192 poll.poll(&mut events, Some(std::time::Duration::from_millis(100)))?;
193
194 for event in events.iter() {
195 match event.token() {
196 SERVER => {
197 // Accept new connections
198 loop {
199 match listener.accept() {
200 Ok((mut stream, addr)) => {
201 debug!("New connection from {:?}", addr);
202
203 // Configure socket
204 stream.set_nodelay(self.config.tcp_nodelay)?;
205
206 let token = Token(next_token);
207 next_token += 1;
208
209 // Register for read events
210 poll.registry().register(
211 &mut stream,
212 token,
213 Interest::READABLE,
214 )?;
215
216 let mut connection = Connection::new_with_addr(
217 0, // fd not used in this path
218 self.config.connection_buffer_size,
219 Arc::clone(&store),
220 &self.config, // Pass config here
221 Some(addr),
222 );
223
224 // Set client registry for CLIENT command support
225 connection.set_client_registry(Arc::clone(&client_registry));
226
227 // Register client in registry
228 client_registry.register(&connection, thread_id);
229
230 connections.insert(token, (stream, connection));
231 self.active_connections.fetch_add(1, Ordering::Relaxed);
232 }
233 Err(e) if e.kind() == ErrorKind::WouldBlock => break,
234 Err(e) => {
235 error!("Error accepting connection: {}", e);
236 break;
237 }
238 }
239 }
240 }
241 token => {
242 // First, collect any deliveries that need to be made
243 let mut deliveries_to_make = Vec::new();
244
245 // Handle client connection
246 let should_close = if let Some((stream, connection)) =
247 connections.get_mut(&token)
248 {
249 let mut should_close = false;
250
251 // Handle writable event - continue writing pending data
252 if event.is_writable() {
253 while let Some(response_data) = connection.pending_writes() {
254 let response_len = response_data.len();
255 match stream.write(response_data) {
256 Ok(n) => {
257 connection.consume_writes(n);
258 if n < response_len {
259 break;
260 }
261 }
262 Err(e) if e.kind() == ErrorKind::WouldBlock => {
263 break;
264 }
265 Err(e) => {
266 error!("Error writing: {}", e);
267 should_close = true;
268 break;
269 }
270 }
271 }
272
273 // If all data written, switch back to READABLE only
274 if connection.pending_writes().is_none() {
275 let _ = poll.registry().reregister(
276 stream,
277 token,
278 Interest::READABLE,
279 );
280 }
281 }
282
283 if event.is_readable() {
284 // Loop to read all available data (important for edge-triggered kqueue)
285 let mut buffer = vec![0u8; 65536];
286 loop {
287 match stream.read(&mut buffer) {
288 Ok(0) => {
289 // Connection closed
290 should_close = true;
291 break;
292 }
293 Ok(n) => {
294 // Process commands inline and get pub/sub operations
295 match connection.process_read(&buffer[..n]) {
296 Ok(pubsub_ops) => {
297 // Process pub/sub operations
298 for op in pubsub_ops {
299 let deliveries = handle_pubsub_operation(
300 &mut pubsub_manager,
301 &pubsub_registry,
302 connection.connection_id,
303 op,
304 connection,
305 thread_id,
306 );
307 deliveries_to_make.extend(deliveries);
308 }
309
310 // Process any queued pub/sub messages
311 connection.process_pubsub_messages();
312
313 // Update client info in registry if needed
314 client_registry.update(connection);
315
316 // Write response immediately
317 while let Some(response_data) =
318 connection.pending_writes()
319 {
320 let response_len = response_data.len();
321 match stream.write(response_data) {
322 Ok(n) => {
323 connection.consume_writes(n);
324 if n < response_len {
325 // Partial write, would block
326 break;
327 }
328 }
329 Err(e)
330 if e.kind()
331 == ErrorKind::WouldBlock =>
332 {
333 break;
334 }
335 Err(e) => {
336 error!("Error writing: {}", e);
337 should_close = true;
338 break;
339 }
340 }
341 }
342
343 // If there's still data to write, register for WRITABLE
344 if connection.pending_writes().is_some() {
345 let _ = poll.registry().reregister(
346 stream,
347 token,
348 Interest::READABLE | Interest::WRITABLE,
349 );
350 }
351 }
352 Err(e) => {
353 error!("Error processing read: {}", e);
354 should_close = true;
355 break;
356 }
357 }
358
359 if connection.is_closed() {
360 should_close = true;
361 break;
362 }
363 }
364 Err(e) if e.kind() == ErrorKind::WouldBlock => {
365 // No more data available, exit read loop
366 break;
367 }
368 Err(e) => {
369 if e.kind() != ErrorKind::ConnectionReset {
370 error!("Error reading: {}", e);
371 }
372 should_close = true;
373 break;
374 }
375 }
376 }
377 }
378
379 should_close
380 } else {
381 false
382 };
383
384 if should_close {
385 if let Some((mut stream, mut connection)) = connections.remove(&token) {
386 let _ = poll.registry().deregister(&mut stream);
387
388 // Clean up pub/sub subscriptions
389 pubsub_manager.connection_dropped(connection.connection_id);
390
391 // Unregister from client registry
392 client_registry.unregister(connection.connection_id);
393
394 connection.close();
395 self.active_connections.fetch_sub(1, Ordering::Relaxed);
396 }
397 }
398
399 // Now deliver any pub/sub messages to local connections
400 for (delivery_conn_id, msg) in deliveries_to_make {
401 for (_, (stream, conn)) in connections.iter_mut() {
402 if conn.connection_id == delivery_conn_id {
403 conn.queue_pubsub_message(msg);
404 conn.process_pubsub_messages();
405
406 // Write the queued messages to the socket
407 while let Some(response_data) = conn.pending_writes() {
408 let response_len = response_data.len();
409 match stream.write(response_data) {
410 Ok(n) => {
411 conn.consume_writes(n);
412 if n < response_len {
413 break;
414 }
415 }
416 Err(e) if e.kind() == ErrorKind::WouldBlock => {
417 break;
418 }
419 Err(e) => {
420 error!("Error writing pub/sub message: {}", e);
421 break;
422 }
423 }
424 }
425 break;
426 }
427 }
428 }
429 }
430 }
431 }
432 }
433
434 // Cleanup
435 for (_, (mut stream, mut connection)) in connections {
436 let _ = poll.registry().deregister(&mut stream);
437 pubsub_manager.connection_dropped(connection.connection_id);
438 client_registry.unregister(connection.connection_id);
439 connection.close();
440 }
441
442 info!("Worker {} shutting down", thread_id);
443 Ok(())
444 }
445}