Repository: Migorithm/duva
Pull request #93 (2024-12-06 18:22:31)

refactor: config set dir


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 5dbfe80e..b007076a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,10 +10,7 @@ use services::{
         interface::{TCancellationNotifier, TCancellationTokenFactory, TRead, TWrite},
         QueryManager,
     },
-    statefuls::{
-        cache::cache_manager::CacheManager,
-        persist::endec::TEnDecoder,
-    },
+    statefuls::{cache::cache_manager::CacheManager, persist::endec::TEnDecoder},
 };
 use std::io::ErrorKind;
 
@@ -35,7 +32,7 @@ pub async fn start_up<T: TCancellationTokenFactory>(
         )
         .await?;
 
-    let config_manager = ConfigManager::run_with_config(config);
+    let config_manager = ConfigManager::run_actor(config);
 
     // Leak the cache_dispatcher to make it static - this is safe because the cache_dispatcher
     // will live for the entire duration of the program.
@@ -43,11 +40,7 @@ pub async fn start_up<T: TCancellationTokenFactory>(
 
     startup_notifier.notify_startup();
 
-    let request_handler = ClientRequestController::new(
-        config_manager,
-        cache_manager,
-        ttl_inbox,
-    );
+    let request_handler = ClientRequestController::new(config_manager, cache_manager, ttl_inbox);
     start_accepting_connections::<T>(listener, request_handler).await
 }
 
@@ -69,7 +62,10 @@ async fn start_accepting_connections<T: TCancellationTokenFactory>(
 }
 
 fn handle_single_user_stream<U: TCancellationTokenFactory>(
-    mut query_manager: QueryManager<impl TWrite + TRead, &'static ClientRequestController<impl TEnDecoder>>,
+    mut query_manager: QueryManager<
+        impl TWrite + TRead,
+        &'static ClientRequestController<impl TEnDecoder>,
+    >,
 ) {
     tokio::spawn(async move {
         loop {
@@ -85,17 +81,17 @@ fn handle_single_user_stream<U: TCancellationTokenFactory>(
             // Notify the cancellation notifier to cancel the query after 100 milliseconds.
             cancellation_notifier.notify();
 
-            let result = query_manager.handle(cancellation_watcher, request, args).await;
+            let result = query_manager
+                .handle(cancellation_watcher, request, args)
+                .await;
             if let Err(e) = result {
                 match e.kind() {
-                    ErrorKind::ConnectionRefused |
-                    ErrorKind::ConnectionReset |
-                    ErrorKind::NetworkUnreachable |
-                    ErrorKind::ConnectionAborted |
-                    ErrorKind::NotConnected |
-                    ErrorKind::NetworkDown |
-                    ErrorKind::BrokenPipe |
-                    ErrorKind::TimedOut => {
+                    ErrorKind::ConnectionRefused
+                    | ErrorKind::ConnectionReset
+                    | ErrorKind::ConnectionAborted
+                    | ErrorKind::NotConnected
+                    | ErrorKind::BrokenPipe
+                    | ErrorKind::TimedOut => {
                         eprintln!("network error: connection closed");
                         break;
                     }
diff --git a/src/services/config/command.rs b/src/services/config/command.rs
index b4b72617..fa2fa548 100644
--- a/src/services/config/command.rs
+++ b/src/services/config/command.rs
@@ -18,7 +18,7 @@ pub enum ConfigResource {
 
 pub enum ConfigResponse {
     Dir(String),
-    DbFileName(Option<String>),
-    FilePath(Option<String>),
+    DbFileName(String),
+    FilePath(String),
     ReplicationInfo(Vec<String>),
 }
diff --git a/src/services/config/config_actor.rs b/src/services/config/config_actor.rs
index 68805a8d..71367f99 100644
--- a/src/services/config/config_actor.rs
+++ b/src/services/config/config_actor.rs
@@ -6,9 +6,9 @@ use tokio::{fs::try_exists, sync::mpsc::Receiver};
 #[derive(Clone)]
 pub struct Config {
     pub port: u16,
-    pub host: String,
-    pub(crate) dir: String,
-    pub dbfilename: Option<String>,
+    pub host: &'static str,
+    pub(crate) dir: &'static str,
+    pub dbfilename: &'static str,
     pub replication: Replication,
     pub(crate) startup_time: SystemTime,
 }
@@ -20,12 +20,12 @@ impl Config {
         while let Some(cmd) = inbox.recv().await {
             match cmd.resource {
                 ConfigResource::Dir => {
-                    let _ = cmd.callback.send(ConfigResponse::Dir(self.dir.clone()));
+                    let _ = cmd.callback.send(ConfigResponse::Dir(self.dir.into()));
                 }
                 ConfigResource::DbFileName => {
                     let _ = cmd
                         .callback
-                        .send(ConfigResponse::DbFileName(self.dbfilename.clone()));
+                        .send(ConfigResponse::DbFileName(self.dbfilename.into()));
                 }
                 ConfigResource::FilePath => {
                     let _ = cmd
@@ -44,37 +44,34 @@ impl Config {
     pub fn bind_addr(&self) -> String {
         format!("{}:{}", self.host, self.port)
     }
-    // The following is used on startup and check if the file exists
-    pub async fn try_filepath(&self) -> Option<String> {
-        match (&self.dir, &self.dbfilename) {
-            (dir, Some(db_filename)) => {
-                let file_path = format!("{}/{}", dir, db_filename);
 
-                match try_exists(&file_path).await {
-                    Ok(true) => Some(file_path),
-                    Ok(false) => {
-                        println!("File does not exist");
-                        None
-                    }
-                    Err(_) => {
-                        println!("Error in try_filepath");
-                        None
-                    }
-                }
-            }
-            // Not given a dbfilename
-            _ => None,
+    pub fn set_dir(&mut self, dir: &str) {
+        unsafe {
+            // Get the pointer to the str
+            let ptr: *const str = self.dir;
+            // Recreate the Box from the pointer and drop it
+            let _reclaimed_box: Box<str> = Box::from_raw(ptr as *mut str);
+            self.dir = Box::leak(dir.into());
         }
     }
-    pub fn get_filepath(&self) -> Option<String> {
-        match (&self.dir, &self.dbfilename) {
-            (dir, Some(db_filename)) => {
-                let file_path = format!("{}/{}", dir, db_filename);
-                Some(file_path)
+    // The following is used on startup and check if the file exists
+    pub async fn try_filepath(&self) -> Option<String> {
+        let file_path = self.get_filepath();
+        match try_exists(&file_path).await {
+            Ok(true) => Some(file_path),
+            Ok(false) => {
+                println!("File does not exist");
+                None
             }
-            _ => None,
+            Err(_) => {
+                println!("Error in try_filepath");
+                None
+            } // Not given a dbfilename
         }
     }
+    pub fn get_filepath(&self) -> String {
+        format!("{}/{}", self.dir, self.dbfilename)
+    }
 }
 
 macro_rules! env_var {
@@ -127,11 +124,11 @@ impl Default for Config {
     fn default() -> Self {
         env_var!(
             {
-                dbfilename,
                 replicaof
             }
             {
                 dir = ".".to_string(),
+                dbfilename = "dump.rdb".to_string(),
                 port = 6379,
                 host = "localhost".to_string()
             }
@@ -147,11 +144,20 @@ impl Default for Config {
 
         Config {
             port,
-            host,
-            dir,
-            dbfilename,
+            host: Box::leak(host.into_boxed_str()),
+            dir: Box::leak(dir.into_boxed_str()),
+            dbfilename: Box::leak(dbfilename.into_boxed_str()),
             replication: Replication::new(replicaof),
             startup_time: SystemTime::now(),
         }
     }
 }
+
+#[test]
+fn test_set_dir() {
+    let mut config = Config::default();
+    config.set_dir("test");
+    assert_eq!(config.dir, "test");
+    config.set_dir("test2");
+    assert_eq!(config.dir, "test2");
+}
diff --git a/src/services/config/config_manager.rs b/src/services/config/config_manager.rs
index 0c752cec..a49b0f7e 100644
--- a/src/services/config/config_manager.rs
+++ b/src/services/config/config_manager.rs
@@ -13,17 +13,12 @@ pub struct ConfigManager(Sender<ConfigQuery>);
 make_smart_pointer!(ConfigManager, Sender<ConfigQuery>);
 
 impl ConfigManager {
-    pub fn run_with_default() -> Self {
-        let config = Config::default();
-        let (tx, inbox) = tokio::sync::mpsc::channel(20);
-        tokio::spawn(config.handle(inbox));
-        Self(tx)
-    }
-    pub fn run_with_config(config: Config) -> Self {
+    pub fn run_actor(config: Config) -> Self {
         let (tx, inbox) = tokio::sync::mpsc::channel(20);
         tokio::spawn(config.handle(inbox));
         Self(tx)
     }
+
     pub async fn route_get(
         &self,
         cmd: (String, String),
@@ -42,7 +37,7 @@ impl ConfigManager {
         let ConfigResponse::FilePath(file_path) = rx.await? else {
             return Err(anyhow::anyhow!("Failed to get file path"));
         };
-        Ok(file_path.unwrap_or("dump.rdb".into()))
+        Ok(file_path)
     }
 
     pub async fn replication_info(&self) -> anyhow::Result<Vec<String>> {
diff --git a/src/services/query_manager/client_request_controllers/mod.rs b/src/services/query_manager/client_request_controllers/mod.rs
index 535502a5..fcb7c7f1 100644
--- a/src/services/query_manager/client_request_controllers/mod.rs
+++ b/src/services/query_manager/client_request_controllers/mod.rs
@@ -29,11 +29,14 @@ where
         cache_manager: &'static CacheManager<U>,
         ttl_manager: TtlSchedulerInbox,
     ) -> &'static Self {
-        Box::leak(ClientRequestController {
-            config_manager,
-            cache_manager,
-            ttl_manager,
-        }.into())
+        Box::leak(
+            ClientRequestController {
+                config_manager,
+                cache_manager,
+                ttl_manager,
+            }
+            .into(),
+        )
     }
 
     pub(crate) async fn handle(
@@ -43,9 +46,7 @@ where
         args: QueryArguments,
     ) -> anyhow::Result<QueryIO> {
         if cancellation_token.watch() {
-            let err = QueryIO::Err(
-                "Error operation cancelled due to timeout".to_string(),
-            );
+            let err = QueryIO::Err("Error operation cancelled due to timeout".to_string());
             return Ok(err);
         }
 
@@ -89,10 +90,7 @@ where
                         QueryIO::BulkString("dir".into()),
                         QueryIO::BulkString(value),
                     ]),
-                    ConfigResponse::DbFileName(value) => match value {
-                        Some(value) => QueryIO::BulkString(value),
-                        None => QueryIO::Null,
-                    },
+                    ConfigResponse::DbFileName(value) => QueryIO::BulkString(value),
                     _ => QueryIO::Err("Invalid operation".into()),
                 }
             }
@@ -104,4 +102,4 @@ where
         };
         Ok(response)
     }
-}
\ No newline at end of file
+}
diff --git a/src/services/query_manager/mod.rs b/src/services/query_manager/mod.rs
index 3cee8bc7..0497b4d6 100644
--- a/src/services/query_manager/mod.rs
+++ b/src/services/query_manager/mod.rs
@@ -1,7 +1,7 @@
-pub mod interface;
-pub mod query_io;
 pub mod client_request_controllers;
+pub mod interface;
 mod query_arguments;
+pub mod query_io;
 
 use crate::services::query_manager::client_request_controllers::client_request::ClientRequest;
 use crate::services::query_manager::client_request_controllers::ClientRequestController;
@@ -19,21 +19,15 @@ where
     T: TWrite + TRead,
 {
     pub(crate) stream: T,
-    pub(crate) controller: U
+    pub(crate) controller: U,
 }
 
 impl<T, U> QueryManager<T, U>
 where
     T: TWrite + TRead,
 {
-    pub(crate) fn new(
-        stream: T,
-        controller: U,
-    ) -> Self {
-        QueryManager {
-            stream,
-            controller
-        }
+    pub(crate) fn new(stream: T, controller: U) -> Self {
+        QueryManager { stream, controller }
     }
 
     // crlf
@@ -60,7 +54,12 @@ where
         let query_io = self.read_value().await?;
         match query_io {
             QueryIO::Array(value_array) => Ok((
-                value_array.first().context("request not given")?.clone().unpack_bulk_str()?.try_into()?,
+                value_array
+                    .first()
+                    .context("request not given")?
+                    .clone()
+                    .unpack_bulk_str()?
+                    .try_into()?,
                 QueryArguments::new(value_array.into_iter().skip(1).collect()),
             )),
             _ => Err(anyhow::anyhow!("Unexpected command format")),
@@ -80,4 +79,4 @@ where
         }
         Ok(())
     }
-}
\ No newline at end of file
+}


Test patch:
diff --git a/tests/test_save_read_dump.rs b/tests/test_save_read_dump.rs
index 2fe24f65..9c122a53 100644
--- a/tests/test_save_read_dump.rs
+++ b/tests/test_save_read_dump.rs
@@ -18,7 +18,13 @@ async fn test_save_read_dump() {
     // GIVEN
     let test_file_name = create_unique_file_name("test_save_dump");
     let mut config = init_config_with_free_port().await;
-    config.dbfilename = Some(test_file_name.clone());
+    let old_ref = config.dbfilename;
+
+    config.dbfilename = Box::leak(Box::new(test_file_name.clone()));
+    unsafe {
+        let ptr: *const str = old_ref;
+        let _ = Box::from_raw(ptr as *mut str);
+    }
 
     let _ = start_test_server::<CancellationToken>(config.clone()).await;