Skip to content

Commit

Permalink
feat: improve NFS mount reliability and filesystem tracking (#133)
Browse files Browse the repository at this point in the history
- Add wait_for_port helper to ensure NFS port is ready before mounting
- Track mount operation timing and add logging
- Store filesystem name in database
- Fix --fs-db-path argument name
- Add timing metrics for mount operations

The main improvement is preventing premature mount attempts that would
cause long retry loops, particularly on macOS. This is achieved by actively
checking port availability before attempting to mount.
  • Loading branch information
appcypher authored Feb 12, 2025
1 parent b6dbe17 commit 1ece270
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 4 deletions.
43 changes: 42 additions & 1 deletion monofs/lib/management/mfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
use sqlx::Row;
use std::path::{Path, PathBuf};
use tokio::net::TcpStream;
use tokio::time;
use tokio::time::Instant;
use tokio::{fs, process::Command};

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -98,7 +101,7 @@ pub async fn init_mfs(mount_dir: Option<PathBuf>) -> FsResult<u32> {
.arg(port.to_string())
.arg("--store-dir")
.arg(&blocks_dir)
.arg("--db-path")
.arg("--fs-db-path")
.arg(&fs_db_path)
.arg("--mount-dir")
.arg(&mount_dir)
Expand Down Expand Up @@ -295,6 +298,10 @@ async fn mount_fs(mount_dir: impl AsRef<Path>, host: &str, port: u32) -> FsResul
}
tracing::info!("Mounting NFS share at {}", mount_dir.display());

// Wait for the port to be ready. If we don't do this, the mount command will retry every
// 5+ seconds on macos.
wait_for_port(host, port).await;

// Construct the mount command
// Using standard NFS mount options:
// - nolocks: disable NFS file locking
Expand All @@ -303,6 +310,7 @@ async fn mount_fs(mount_dir: impl AsRef<Path>, host: &str, port: u32) -> FsResul
// - soft: return errors rather than hang on timeouts
// - mountport=port: use same port for mount protocol
let source = format!("{}:/", host);
let start = Instant::now();
let status = Command::new("mount")
.arg("-t")
.arg("nfs")
Expand All @@ -316,6 +324,8 @@ async fn mount_fs(mount_dir: impl AsRef<Path>, host: &str, port: u32) -> FsResul
.status()
.await?;

tracing::info!("Mount command took {:?} to complete", start.elapsed());

if !status.success() {
return Err(FsError::MountFailed(format!(
"Mount command exited with status: {}",
Expand All @@ -326,3 +336,34 @@ async fn mount_fs(mount_dir: impl AsRef<Path>, host: &str, port: u32) -> FsResul
tracing::info!("Successfully mounted NFS share at {}", mount_dir.display());
Ok(())
}

//--------------------------------------------------------------------------------------------------
// Functions: Helpers
//--------------------------------------------------------------------------------------------------

/// Wait for the given host and port to become available.
///
/// This function tries to open a TCP connection to the address. If it fails,
/// it waits 50ms and tries again.
async fn wait_for_port(host: &str, port: u32) {
let addr = format!("{}:{}", host, port);
loop {
match TcpStream::connect(&addr).await {
Ok(_) => {
tracing::info!("Port {} on {} is ready!", port, host);
break;
}
Err(e) => {
let retry_delay = 50;
tracing::info!(
"Port {} on {} is not ready yet (error: {}), retrying in {}ms...",
port,
host,
e,
retry_delay
);
time::sleep(time::Duration::from_millis(retry_delay)).await;
}
}
}
}
7 changes: 4 additions & 3 deletions monofs/lib/runtime/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl ProcessMonitor for NfsServerMonitor {
mut stderr: ChildStderr,
) -> MonoutilsResult<()> {
// Setup child's log
let log_name = self.generate_log_name(pid, name);
let log_name = self.generate_log_name(pid, &name);
let log_path = self.log_dir.join(&log_name);

let nfs_server_log = RotatingLog::new(&log_path).await?;
Expand All @@ -100,10 +100,11 @@ impl ProcessMonitor for NfsServerMonitor {
// Insert filesystem entry into fs_db
sqlx::query(
r#"
INSERT INTO filesystems (mount_dir, supervisor_pid, nfsserver_pid)
VALUES (?, ?, ?)
INSERT INTO filesystems (name, mount_dir, supervisor_pid, nfsserver_pid)
VALUES (?, ?, ?, ?)
"#,
)
.bind(name)
.bind(self.mount_dir.to_string_lossy().to_string())
.bind(self.supervisor_pid)
.bind(pid)
Expand Down

0 comments on commit 1ece270

Please sign in to comment.