Browse Source

Allow configuring the number of days to expire documents (#18)

* Allow configuring the number of days to expire documents

* Update server tests about server data

* Update test_cleanup for testing a different expiry value

* Remove unnecessary Arc<> while passing expiry_days to cleaner

* Rename `ServerData` to `ServerConfig`

* Update tests to rename the `ServerData`

* Update rustpad-server/tests/cleanup.rs

Co-authored-by: Eric Zhang <ekzhang1@gmail.com>
Orhun Parmaksız 4 years ago
parent
commit
f50f987626

+ 1 - 0
.env

@@ -1,2 +1,3 @@
 # Environment variables for development
 # Environment variables for development
 RUST_LOG=info
 RUST_LOG=info
+EXPIRY_DAYS=1

+ 23 - 8
rustpad-server/src/lib.rs

@@ -44,9 +44,25 @@ struct Stats {
     num_documents: usize,
     num_documents: usize,
 }
 }
 
 
+/// Server configuration.
+#[derive(Debug)]
+pub struct ServerConfig {
+    /// Number of days to clean up documents after inactivity.
+    pub expiry_days: u32,
+}
+
+impl Default for ServerConfig {
+    fn default() -> Self {
+        Self { expiry_days: 1 }
+    }
+}
+
 /// A combined filter handling all server routes.
 /// A combined filter handling all server routes.
-pub fn server() -> BoxedFilter<(impl Reply,)> {
-    warp::path("api").and(backend()).or(frontend()).boxed()
+pub fn server(config: ServerConfig) -> BoxedFilter<(impl Reply,)> {
+    warp::path("api")
+        .and(backend(config))
+        .or(frontend())
+        .boxed()
 }
 }
 
 
 /// Construct routes for static files from React.
 /// Construct routes for static files from React.
@@ -55,9 +71,9 @@ fn frontend() -> BoxedFilter<(impl Reply,)> {
 }
 }
 
 
 /// Construct backend routes, including WebSocket handlers.
 /// Construct backend routes, including WebSocket handlers.
-fn backend() -> BoxedFilter<(impl Reply,)> {
+fn backend(config: ServerConfig) -> BoxedFilter<(impl Reply,)> {
     let state: Arc<DashMap<String, Document>> = Default::default();
     let state: Arc<DashMap<String, Document>> = Default::default();
-    tokio::spawn(cleaner(Arc::clone(&state)));
+    tokio::spawn(cleaner(Arc::clone(&state), config.expiry_days));
 
 
     let state_filter = warp::any().map(move || Arc::clone(&state));
     let state_filter = warp::any().map(move || Arc::clone(&state));
 
 
@@ -106,15 +122,14 @@ fn backend() -> BoxedFilter<(impl Reply,)> {
 }
 }
 
 
 const HOUR: Duration = Duration::from_secs(3600);
 const HOUR: Duration = Duration::from_secs(3600);
-const DAY: Duration = Duration::from_secs(24 * 3600);
 
 
-// Reclaims memory for documents after a day of inactivity.
-async fn cleaner(state: Arc<DashMap<String, Document>>) {
+// Reclaims memory for documents.
+async fn cleaner(state: Arc<DashMap<String, Document>>, expiry_days: u32) {
     loop {
     loop {
         time::sleep(HOUR).await;
         time::sleep(HOUR).await;
         let mut keys = Vec::new();
         let mut keys = Vec::new();
         for entry in &*state {
         for entry in &*state {
-            if entry.last_accessed.elapsed() > DAY {
+            if entry.last_accessed.elapsed() > HOUR * 24 * expiry_days {
                 keys.push(entry.key().clone());
                 keys.push(entry.key().clone());
             }
             }
         }
         }

+ 9 - 2
rustpad-server/src/main.rs

@@ -1,4 +1,4 @@
-use rustpad_server::server;
+use rustpad_server::{server, ServerConfig};
 
 
 #[tokio::main]
 #[tokio::main]
 async fn main() {
 async fn main() {
@@ -10,5 +10,12 @@ async fn main() {
         .parse()
         .parse()
         .expect("Unable to parse PORT");
         .expect("Unable to parse PORT");
 
 
-    warp::serve(server()).run(([0, 0, 0, 0], port)).await;
+    let config = ServerConfig {
+        expiry_days: std::env::var("EXPIRY_DAYS")
+            .unwrap_or_else(|_| String::from("1"))
+            .parse()
+            .expect("Unable to parse EXPIRY_DAYS"),
+    };
+
+    warp::serve(server(config)).run(([0, 0, 0, 0], port)).await;
 }
 }

+ 6 - 3
rustpad-server/tests/cleanup.rs

@@ -5,7 +5,7 @@ use std::time::Duration;
 use anyhow::Result;
 use anyhow::Result;
 use common::*;
 use common::*;
 use operational_transform::OperationSeq;
 use operational_transform::OperationSeq;
-use rustpad_server::server;
+use rustpad_server::{server, ServerConfig};
 use serde_json::json;
 use serde_json::json;
 use tokio::time;
 use tokio::time;
 
 
@@ -14,7 +14,10 @@ pub mod common;
 #[tokio::test]
 #[tokio::test]
 async fn test_cleanup() -> Result<()> {
 async fn test_cleanup() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig {
+        expiry_days: 2,
+        ..ServerConfig::default()
+    });
 
 
     expect_text(&filter, "old", "").await;
     expect_text(&filter, "old", "").await;
 
 
@@ -39,7 +42,7 @@ async fn test_cleanup() -> Result<()> {
 
 
     let hour = Duration::from_secs(3600);
     let hour = Duration::from_secs(3600);
     time::pause();
     time::pause();
-    time::advance(23 * hour).await;
+    time::advance(47 * hour).await;
     expect_text(&filter, "old", "hello").await;
     expect_text(&filter, "old", "hello").await;
 
 
     time::advance(3 * hour).await;
     time::advance(3 * hour).await;

+ 5 - 5
rustpad-server/tests/sockets.rs

@@ -6,7 +6,7 @@ use anyhow::Result;
 use common::*;
 use common::*;
 use log::info;
 use log::info;
 use operational_transform::OperationSeq;
 use operational_transform::OperationSeq;
-use rustpad_server::server;
+use rustpad_server::{server, ServerConfig};
 use serde_json::json;
 use serde_json::json;
 use tokio::time;
 use tokio::time;
 
 
@@ -15,7 +15,7 @@ pub mod common;
 #[tokio::test]
 #[tokio::test]
 async fn test_single_operation() -> Result<()> {
 async fn test_single_operation() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     expect_text(&filter, "foobar", "").await;
     expect_text(&filter, "foobar", "").await;
 
 
@@ -54,7 +54,7 @@ async fn test_single_operation() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_invalid_operation() -> Result<()> {
 async fn test_invalid_operation() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     expect_text(&filter, "foobar", "").await;
     expect_text(&filter, "foobar", "").await;
 
 
@@ -80,7 +80,7 @@ async fn test_invalid_operation() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_concurrent_transform() -> Result<()> {
 async fn test_concurrent_transform() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     // Connect the first client
     // Connect the first client
     let mut client = connect(&filter, "foobar").await?;
     let mut client = connect(&filter, "foobar").await?;
@@ -199,7 +199,7 @@ async fn test_concurrent_transform() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_set_language() -> Result<()> {
 async fn test_set_language() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     let mut client = connect(&filter, "foobar").await?;
     let mut client = connect(&filter, "foobar").await?;
     let msg = client.recv().await?;
     let msg = client.recv().await?;

+ 3 - 3
rustpad-server/tests/stress.rs

@@ -6,7 +6,7 @@ use anyhow::{anyhow, Result};
 use common::*;
 use common::*;
 use log::info;
 use log::info;
 use operational_transform::OperationSeq;
 use operational_transform::OperationSeq;
-use rustpad_server::server;
+use rustpad_server::{server, ServerConfig};
 use serde_json::{json, Value};
 use serde_json::{json, Value};
 use tokio::time::Instant;
 use tokio::time::Instant;
 
 
@@ -15,7 +15,7 @@ pub mod common;
 #[tokio::test]
 #[tokio::test]
 async fn test_lost_wakeups() -> Result<()> {
 async fn test_lost_wakeups() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     expect_text(&filter, "stress", "").await;
     expect_text(&filter, "stress", "").await;
 
 
@@ -74,7 +74,7 @@ async fn test_lost_wakeups() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_large_document() -> Result<()> {
 async fn test_large_document() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     expect_text(&filter, "stress", "").await;
     expect_text(&filter, "stress", "").await;
 
 

+ 5 - 5
rustpad-server/tests/users.rs

@@ -2,7 +2,7 @@
 
 
 use anyhow::Result;
 use anyhow::Result;
 use common::*;
 use common::*;
-use rustpad_server::server;
+use rustpad_server::{server, ServerConfig};
 use serde_json::json;
 use serde_json::json;
 
 
 pub mod common;
 pub mod common;
@@ -10,7 +10,7 @@ pub mod common;
 #[tokio::test]
 #[tokio::test]
 async fn test_two_users() -> Result<()> {
 async fn test_two_users() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     let mut client = connect(&filter, "foobar").await?;
     let mut client = connect(&filter, "foobar").await?;
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
@@ -54,7 +54,7 @@ async fn test_two_users() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_invalid_user() -> Result<()> {
 async fn test_invalid_user() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     let mut client = connect(&filter, "foobar").await?;
     let mut client = connect(&filter, "foobar").await?;
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
@@ -69,7 +69,7 @@ async fn test_invalid_user() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_leave_rejoin() -> Result<()> {
 async fn test_leave_rejoin() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     let mut client = connect(&filter, "foobar").await?;
     let mut client = connect(&filter, "foobar").await?;
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
@@ -114,7 +114,7 @@ async fn test_leave_rejoin() -> Result<()> {
 #[tokio::test]
 #[tokio::test]
 async fn test_cursors() -> Result<()> {
 async fn test_cursors() -> Result<()> {
     pretty_env_logger::try_init().ok();
     pretty_env_logger::try_init().ok();
-    let filter = server();
+    let filter = server(ServerConfig::default());
 
 
     let mut client = connect(&filter, "foobar").await?;
     let mut client = connect(&filter, "foobar").await?;
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));
     assert_eq!(client.recv().await?, json!({ "Identity": 0 }));