From 5efef83a90ad3ea5e1d339c8426e9e096265e8f6 Mon Sep 17 00:00:00 2001 From: grabbit Date: Thu, 19 Feb 2026 14:15:23 +0800 Subject: [PATCH] Add multi_thread_streams/cutoff support and Samba performance tuning - Add multi_thread_streams (default 4) and multi_thread_cutoff (default "50M") fields to ReadConfig, wired into rclone mount args - Expose both fields in Web UI config editor under Read Tuning section - Add Samba performance options: TCP_NODELAY, large readwrite, max xmit - Update config.toml.default with new fields and sftp_connections guidance Co-Authored-By: Claude Sonnet 4.6 --- src/config.rs | 18 ++++++++++++++++++ src/rclone/mount.rs | 9 +++++++++ src/services/samba.rs | 10 ++++++++++ templates/config.toml.default | 7 ++++++- templates/web/tabs/config.html | 8 ++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 44b2104..9d275b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -118,6 +118,12 @@ pub struct ReadConfig { /// In-memory buffer size (e.g. "256M"). #[serde(default = "default_buffer_size")] pub buffer_size: String, + /// Number of parallel SFTP streams for single-file downloads (rclone --multi-thread-streams). + #[serde(default = "default_multi_thread_streams")] + pub multi_thread_streams: u32, + /// Minimum file size to trigger multi-thread download (e.g. "50M"). + #[serde(default = "default_multi_thread_cutoff")] + pub multi_thread_cutoff: String, } /// Bandwidth control. @@ -292,6 +298,12 @@ fn default_read_ahead() -> String { fn default_buffer_size() -> String { "256M".into() } +fn default_multi_thread_streams() -> u32 { + 4 +} +fn default_multi_thread_cutoff() -> String { + "50M".into() +} fn default_bw_zero() -> String { "0".into() } @@ -417,6 +429,8 @@ impl Config { writeln!(out, "chunk_limit = {:?}", self.read.chunk_limit).unwrap(); writeln!(out, "read_ahead = {:?}", self.read.read_ahead).unwrap(); writeln!(out, "buffer_size = {:?}", self.read.buffer_size).unwrap(); + writeln!(out, "multi_thread_streams = {}", self.read.multi_thread_streams).unwrap(); + writeln!(out, "multi_thread_cutoff = {:?}", self.read.multi_thread_cutoff).unwrap(); writeln!(out).unwrap(); // --- Bandwidth --- @@ -690,6 +704,8 @@ mount_point = "/mnt/photos" assert_eq!(config.read.chunk_limit, "1G"); assert_eq!(config.read.read_ahead, "512M"); assert_eq!(config.read.buffer_size, "256M"); + assert_eq!(config.read.multi_thread_streams, 4); + assert_eq!(config.read.multi_thread_cutoff, "50M"); assert_eq!(config.bandwidth.limit_up, "0"); assert_eq!(config.bandwidth.limit_down, "0"); @@ -994,6 +1010,8 @@ mount_point = "/mnt/photos" assert_eq!(config.cache.dir, config2.cache.dir); assert_eq!(config.cache.max_size, config2.cache.max_size); assert_eq!(config.read.chunk_size, config2.read.chunk_size); + assert_eq!(config.read.multi_thread_streams, config2.read.multi_thread_streams); + assert_eq!(config.read.multi_thread_cutoff, config2.read.multi_thread_cutoff); assert_eq!(config.bandwidth.adaptive, config2.bandwidth.adaptive); assert_eq!(config.writeback.transfers, config2.writeback.transfers); assert_eq!(config.protocols.enable_smb, config2.protocols.enable_smb); diff --git a/src/rclone/mount.rs b/src/rclone/mount.rs index d9e2570..0b8aa50 100644 --- a/src/rclone/mount.rs +++ b/src/rclone/mount.rs @@ -66,6 +66,13 @@ pub fn build_mount_args(config: &Config, share: &ShareConfig, rc_port: u16) -> V args.push("--vfs-read-ahead".into()); args.push(config.read.read_ahead.clone()); + // Multi-thread download: splits large files across N parallel SFTP streams + args.push("--multi-thread-streams".into()); + args.push(config.read.multi_thread_streams.to_string()); + + args.push("--multi-thread-cutoff".into()); + args.push(config.read.multi_thread_cutoff.clone()); + // Concurrent transfers for write-back args.push("--transfers".into()); args.push(config.writeback.transfers.to_string()); @@ -240,6 +247,8 @@ mount_point = "/mnt/photos" assert!(args.contains(&"--dir-cache-time".to_string())); assert!(args.contains(&"1h".to_string())); assert!(args.contains(&"--buffer-size".to_string())); + assert!(args.contains(&"--multi-thread-streams".to_string())); + assert!(args.contains(&"--multi-thread-cutoff".to_string())); assert!(args.contains(&"--transfers".to_string())); assert!(args.contains(&"4".to_string())); assert!(args.contains(&"--rc".to_string())); diff --git a/src/services/samba.rs b/src/services/samba.rs index 3063675..1817073 100644 --- a/src/services/samba.rs +++ b/src/services/samba.rs @@ -53,6 +53,13 @@ pub fn generate(config: &Config) -> Result { writeln!(conf, " printcap name = /dev/null")?; writeln!(conf, " disable spoolss = yes")?; writeln!(conf)?; + writeln!(conf, " # Performance tuning")?; + writeln!(conf, " socket options = TCP_NODELAY IPTOS_THROUGHPUT SO_RCVBUF=131072 SO_SNDBUF=131072")?; + writeln!(conf, " read raw = yes")?; + writeln!(conf, " write raw = yes")?; + writeln!(conf, " large readwrite = yes")?; + writeln!(conf, " max xmit = 65535")?; + writeln!(conf)?; // Share sections — each share points at its own mount_point for share in &config.shares { @@ -274,6 +281,9 @@ mount_point = "/mnt/photos" assert!(content.contains("server min protocol = SMB2_02")); assert!(content.contains("map to guest = Bad User")); assert!(content.contains("load printers = no")); + assert!(content.contains("socket options = TCP_NODELAY")); + assert!(content.contains("large readwrite = yes")); + assert!(content.contains("max xmit = 65535")); } #[test] diff --git a/templates/config.toml.default b/templates/config.toml.default index 2b27dce..c050c08 100644 --- a/templates/config.toml.default +++ b/templates/config.toml.default @@ -18,7 +18,7 @@ nas_user = "admin" # nas_key_file = "/root/.ssh/id_ed25519" # SFTP port sftp_port = 22 -# SFTP connection pool size +# SFTP connection pool size (if multi_thread_streams=4, recommend >= 16) sftp_connections = 8 # --- Additional NAS (uncomment to add) --- @@ -49,6 +49,11 @@ chunk_limit = "1G" read_ahead = "512M" # In-memory buffer size buffer_size = "256M" +# Number of parallel SFTP streams for single-file downloads (improves cold-read speed) +# If using multi_thread_streams=4, set sftp_connections >= 16 for multi-file concurrency +multi_thread_streams = 4 +# Minimum file size to trigger multi-thread download +multi_thread_cutoff = "50M" [bandwidth] # Upload (write-back) speed limit ("0" = unlimited) diff --git a/templates/web/tabs/config.html b/templates/web/tabs/config.html index a2570d3..d7630a2 100644 --- a/templates/web/tabs/config.html +++ b/templates/web/tabs/config.html @@ -295,6 +295,14 @@ if (window.Alpine) { +
+ + +
+
+ + +