# MarkBase FUSE System Design ## Overview **Objective**: Implement virtual file system mount for MarkBase users using FUSE technology, enabling direct file access through macOS Finder and video editing software. **Target Performance**: 600MB/s sustained write per user, supporting 10 concurrent users. **Technology Choice**: FUSE-T (Kext-less FUSE for macOS) --- ## FUSE-T vs macFUSE Comparison ### Core Architecture | Feature | FUSE-T | macFUSE | |---------|---------|---------| | **Kernel Design** | Kext-less (userspace server) | Kernel Extension + FSKit (macOS 26+) | | **Backend Protocol** | NFSv4 / SMB3 / FSKit | Direct kernel FUSE API | | **Installation** | Simple (brew install) | Requires System Settings → Privacy & Security | | **Stability** | Stable (userspace server) | Potential kernel crash/lock-up | | **License** | Free personal use, commercial license required | Open source (BSD-style) | | **macOS Support** | All versions | macOS 12+ | | **App Store** | Embeddable | Requires special handling | | **API Compatibility** | libfuse2/libfuse3 | libfuse2/libfuse3 + macFUSE.framework | | **Install Count** | 21,892 (365 days) | 129,388 (365 days) | ### Technical Flow **FUSE-T Operation:** ``` User App → libfuse → FUSE-T Server (userspace) → NFS/SMB/FSKit → macOS mount ``` **macFUSE Operation (Legacy):** ``` User App → libfuse → macFUSE kext (kernel) → VFS → macOS mount ``` **macFUSE Operation (macOS 26+):** ``` User App → libfuse → FSKit (userspace) → macOS mount ``` ### Performance Considerations | Factor | Impact | |--------|--------| | FUSE-T NFS backend | Extra TCP/IP overhead (~5-10% latency) | | macFUSE kext | Direct kernel path (fastest) | | macFUSE FSKit | Userspace path (similar to FUSE-T) | | Network packet handling | FUSE-T requires NFS RPC conversion | | Large file writes | Both limited by userspace buffer | ### Recommendation **FUSE-T is recommended for MarkBase:** 1. **Stability Priority**: Avoid kernel panic risk 2. **Deployment Friendly**: No Security Settings configuration needed 3. **macOS 26 Support**: FSKit backend option (same as macFUSE) 4. **Commercial Distribution**: Controllable licensing cost **macFUSE suitable for:** - Maximum raw performance (kernel bypass) - Existing stable kernel extension environment - Open source projects (no commercial licensing) --- ## Backend Selection ### Backend Types | Backend | Protocol | macOS Support | Performance | Stability | |---------|----------|---------------|-------------|-----------| | **NFSv4** | NFS v4 over TCP | All versions | ~5-10% overhead | Very stable | | **SMB3** | SMB 3.0 over TCP | All versions | ~8-12% overhead | Stable | | **FSKit** | Apple FSKit API | macOS 26+ | Direct path | Native | ### Backend Architecture ```rust pub enum BackendType { Nfs4, // NFSv4 backend (all macOS support) Fskit, // FSKit backend (macOS 26+) } impl BackendType { pub fn mount_options(&self) -> Vec { match self { BackendType::Nfs4 => vec![ MountOption::Backend("nfs"), MountOption::AutoUnmount, ], BackendType::Fskit => vec![ MountOption::Backend("fskit"), MountOption::AutoUnmount, ], } } } ``` ### Performance Comparison **Expected Throughput (4K ProRes 4444 Write):** | Backend | Expected Speed | Overhead | Recommendation | |---------|----------------|----------|----------------| | NFSv4 | 550-600 MB/s | 5-10% | Stable baseline | | FSKit | 600-700 MB/s | Minimal | Performance target | --- ## Module Architecture ### Directory Structure ``` src/fuse/ ├── mod.rs # FUSE core module (entry point) ├── filesystem.rs # MarkBaseFs implementation ├── handlers.rs # FUSE operation handlers ├── backend.rs # Backend selection (NFSv4/FSKit) ├── cache.rs # LRU cache for metadata └── mount_manager.rs # Multi-user concurrent mount ``` ### Module Dependencies ```toml # Cargo.toml additions [dependencies] fuse = "0.3" # FUSE-T Rust bindings (or libfuse3) time = "0.3" # Timestamp handling lru = "0.12" # LRU cache implementation uuid = "1.11" # UUID handling tokio = { version = "1", features = ["full"] } ``` --- ## Core Components ### 1. MarkBaseFs (filesystem.rs) **Purpose**: Main filesystem implementation backed by SQLite ```rust pub struct MarkBaseFs { user_id: String, db: Connection, backend: BackendType, // Caches attr_cache: LruCache, path_cache: LruCache, dir_cache: LruCache>, // Write buffer write_buffer: HashMap>, buffer_size: usize, // Default: 64KB } impl Filesystem for MarkBaseFs { // Operations implementation... } ``` **Key Operations:** | Operation | Handler | Database Query | Cache Strategy | |-----------|---------|----------------|----------------| | `getattr()` | Get file/directory attributes | `SELECT * FROM file_nodes WHERE node_id = ?` | LRU cache (10,000 entries) | | `readdir()` | List directory contents | `SELECT * FROM file_nodes WHERE parent_id = ?` | LRU cache (1,000 entries) | | `read()` | Read file content | `SELECT location FROM file_locations WHERE file_uuid = ?` | Direct I/O (no cache) | | `write()` | Write file content | `INSERT INTO file_locations` | Buffer 64KB chunks | | `lookup()` | Find file by name | `SELECT node_id FROM file_nodes WHERE parent_id = ? AND label = ?` | LRU cache (10,000 entries) | | `create()` | Create new file | `INSERT INTO file_nodes` | Invalidate parent cache | | `unlink()` | Delete file | `DELETE FROM file_nodes WHERE node_id = ?` | Invalidate parent cache | ### 2. Backend Selection (backend.rs) **Purpose**: Choose optimal backend based on macOS version ```rust pub fn select_backend() -> BackendType { let os_version = System::os_version(); if os_version >= "26.0" { // macOS 26+ supports FSKit (native, fastest) BackendType::Fskit } else { // Older macOS uses NFSv4 (stable) BackendType::Nfs4 } } ``` ### 3. Cache Management (cache.rs) **Purpose**: Reduce SQLite query overhead ```rust pub struct FuseCache { attr_cache: LruCache, path_cache: LruCache, ttl: Duration, // Time-to-live: 60 seconds } #[derive(Clone)] struct CachedAttr { attr: FileAttr, cached_at: Instant, } impl FuseCache { pub fn get_attr(&mut self, ino: u64) -> Option { if let Some(cached) = self.attr_cache.get(&ino) { if cached.cached_at.elapsed() < self.ttl { return Some(cached.attr.clone()); } } None } pub fn put_attr(&mut self, ino: u64, attr: FileAttr) { self.attr_cache.put(ino, CachedAttr { attr, cached_at: Instant::now(), }); } } ``` ### 4. Mount Manager (mount_manager.rs) **Purpose**: Handle multi-user concurrent mounts ```rust use tokio::task::JoinSet; pub struct MountManager { mounts: HashMap, backend: BackendType, } pub struct MountedFs { user_id: String, mount_path: PathBuf, process: JoinHandle>, } impl MountManager { pub async fn mount_user(&mut self, user_id: String, base_dir: PathBuf) -> Result<()> { let mount_path = base_dir.join(format!("MarkBase_{}", user_id)); let db_path = FileTree::user_db_path(&user_id); let conn = FileTree::open_user_db(&db_path)?; let backend = self.backend.clone(); let fs = MarkBaseFs::new(user_id.clone(), conn, backend); let handle = tokio::spawn(async move { fuse::mount(fs, &mount_path, &backend.mount_options())?; Ok(()) }); self.mounts.insert(user_id, MountedFs { user_id, mount_path, process: handle, }); Ok(()) } pub async fn mount_all(&mut self, users: Vec, base_dir: PathBuf) -> Result<()> { let mut tasks = JoinSet::new(); for user_id in users { tasks.spawn(async move { MountManager::new().mount_user(user_id, base_dir).await }); } while let Some(result) = tasks.join_next().await { result??; } Ok(()) } pub async fn unmount_all(&mut self) -> Result<()> { for mount in self.mounts.values() { fuse::unmount(&mount.mount_path)?; } self.mounts.clear(); Ok(()) } } ``` --- ## Performance Optimization ### Strategy 1: Write Buffering **Problem**: FUSE write() syscall overhead **Solution**: Buffer writes in 64KB chunks ```rust impl MarkBaseFs { fn write(&mut self, ino: u64, offset: u64, data: &[u8], reply: ReplyWrite) { // Accumulate in buffer let buffer = self.write_buffer.entry(ino).or_insert(Vec::new()); buffer.extend_from_slice(data); // Flush when buffer >= 64KB if buffer.len() >= 64 * 1024 { self.flush_buffer(ino); } reply.written(data.len() as u32); } fn flush_buffer(&mut self, ino: u64) { if let Some(buffer) = self.write_buffer.get(&ino) { let path = self.get_file_path(ino); std::fs::write(&path, buffer)?; self.write_buffer.remove(&ino); } } } ``` ### Strategy 2: Metadata Caching **Problem**: SQLite query latency (~2-5ms per query) **Solution**: LRU cache with 60s TTL **Cache Configuration:** | Cache Type | Size | TTL | Hit Rate Target | |------------|------|-----|-----------------| | attr_cache | 10,000 entries | 60s | 95% | | path_cache | 10,000 entries | 60s | 90% | | dir_cache | 1,000 entries | 60s | 85% | ### Strategy 3: FSKit Backend **Problem**: NFSv4 TCP/IP overhead (~5-10%) **Solution**: Use FSKit backend on macOS 26+ **Performance Impact:** | Metric | NFSv4 | FSKit | Improvement | |--------|-------|-------|-------------| | Write latency | 15ms | 8ms | 47% reduction | | Read latency | 10ms | 5ms | 50% reduction | | Throughput | 550 MB/s | 650 MB/s | 18% increase | --- ## Multi-User Concurrent Mount ### Architecture ``` MountManager ├── mount_user(user_id) → spawn tokio task ├── mount_all([user1, user2, ...]) → parallel mount ├── unmount_user(user_id) → graceful shutdown └── unmount_all() → cleanup all mounts ``` ### Mount Paths ``` /Volumes/ ├── MarkBase_warren/ → data/users/warren.sqlite ├── MarkBase_momentry/ → data/users/momentry.sqlite ├── MarkBase_demo/ → data/users/demo.sqlite ├── MarkBase_user1/ → data/users/user1.sqlite ... └── MarkBase_user10/ → data/users/user10.sqlite ``` ### Concurrent Strategy ```rust // Parallel mount using tokio::JoinSet pub async fn mount_all(&mut self, users: Vec) -> Result<()> { let mut tasks = JoinSet::new(); for user_id in users { tasks.spawn(async { MountManager::new().mount_user(user_id).await }); } // Collect results let mut results = Vec::new(); while let Some(result) = tasks.join_next().await { results.push(result?); } Ok(()) } ``` ### Performance Target | Metric | Target | Measurement | |--------|--------|-------------| | Mount latency (single user) | <100ms | Time from mount() to ready | | Mount latency (10 users) | <2s | Parallel mount completion | | Mount stability | 24h uptime | No crash/lock-up | | Concurrent writes | 10 × 600MB/s | AJA System Test | --- ## Database Integration ### File Node Mapping **UUID to inode mapping:** ```rust fn uuid_to_ino(uuid: &str) -> u64 { // Use first 8 bytes of UUID as inode number let bytes = uuid.as_bytes(); u64::from_be_bytes(&bytes[0..8]) } fn ino_to_uuid(ino: u64) -> String { // Convert inode back to UUID (with padding) let bytes = ino.to_be_bytes(); format!("{:016x}{}", bytes, "00000000-0000-0000-0000-000000000000") } ``` ### Path Resolution **Virtual path construction:** ```rust fn build_virtual_path(&self, ino: u64) -> PathBuf { let mut path = PathBuf::new(); let mut current_ino = ino; // Walk up parent chain while current_ino != 0 { let node = self.get_node(current_ino)?; path.push(node.label); current_ino = node.parent_id; } path } ``` ### File Location Query ```rust fn get_file_path(&self, ino: u64) -> PathBuf { let uuid = self.ino_to_uuid(ino); self.db.query_row( "SELECT location FROM file_locations WHERE file_uuid = ?", [uuid], |row| row.get::<_, String>(0) ).map(PathBuf::from) } ``` --- ## CLI Commands ### Mount Commands ```bash # Single user mount cargo run -- fuse --mount --user warren --dir /Volumes/MarkBase_warren # Multi-user concurrent mount cargo run -- fuse --mount --all --dir /Volumes/ # Specify backend cargo run -- fuse --mount --user warren --backend fskit # Unmount cargo run -- fuse --unmount --dir /Volumes/MarkBase_warren cargo run -- fuse --unmount --all ``` ### Test Commands ```bash # Performance test cargo run -- fuse --test --user warren --size 6GB # AJA System Test simulation cargo run -- fuse --test --aja --config 4K_ProRes # Stability test cargo run -- fuse --test --stability --duration 24h ``` --- ## Testing Strategy ### Phase 1: POC Verification **Tests:** 1. Hello FUSE mount/unmount 2. Basic read/write operations 3. Backend selection (NFSv4 vs FSKit) ### Phase 2: SQLite-backed FUSE **Tests:** 1. warren user mount (12,659 nodes) 2. Directory traversal 3. File read/write 4. Metadata caching ### Phase 3: Multi-user Concurrent **Tests:** 1. 10 user parallel mount 2. Concurrent writes (AJA System Test) 3. 24h stability test 4. Unmount/shutdown ### Phase 4: Performance Validation **Tests:** 1. AJA System Test 4K ProRes 4444 (600MB/s target) 2. dd baseline comparison 3. FSKit backend performance 4. Cache effectiveness (hit rate measurement) --- ## Risk Assessment | Risk | Probability | Impact | Mitigation | |------|-------------|--------|------------| | FUSE-T installation failure | Low | High | Document brew installation steps | | NFSv4 performance bottleneck | Medium | Medium | FSKit backend fallback | | SQLite query latency | Medium | High | LRU caching + connection pooling | | Kernel panic (macFUSE only) | Low | Critical | Use FUSE-T (kext-less) | | Multi-user mount deadlock | Low | High | Async mount + timeout handling | | Write buffer overflow | Low | Medium | Chunked flush + memory limit | --- ## Performance Targets Summary | Metric | Target | Measurement Method | |--------|--------|---------------------| | Write throughput | >=600MB/s | AJA System Test 4K ProRes 4444 | | Read throughput | >=800MB/s | AJA System Test 4K ProRes 422 HQ | | Mount latency (single) | <100ms | Timing measurement | | Mount latency (10 users) | <2s | Parallel mount timing | | Concurrent writes | 10 × 600MB/s | AJA concurrent test | | Uptime stability | 24h no crash | Stability test | | Cache hit rate | >=90% | Cache statistics | --- **Last Updated**: 2026-05-17 **Version**: 1.0 **Status**: Design Complete, Ready for POC Implementation