{"record":{"id":"4c745d500c127cd8","repo":"clockworklabs/SpacetimeDB","slug":"no-space-left-on-device","errorCode":null,"errorMessage":"no space left on device","messagePattern":"no space left on device","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/commitlog/src/repo/mem/segment.rs","lineNumber":252,"sourceCode":"        if *avail == 0 {\n            return Err(enospc());\n        }\n\n        let want = size.next_multiple_of(PAGE_SIZE as u64) - storage.alloc;\n        let have = want.min(*avail);\n        storage.alloc += have;\n        *avail -= have;\n\n        if want > have {\n            return Err(enospc());\n        }\n\n        Ok(())\n    }\n}\n\nfn enospc() -> io::Error {\n    io::Error::new(io::ErrorKind::StorageFull, \"no space left on device\")\n}\n\n#[cfg(feature = \"streaming\")]\nmod async_impls {\n    use super::*;\n\n    use std::{\n        io::{Read as _, Seek as _, Write as _},\n        pin::Pin,\n        task::{Context, Poll},\n    };\n\n    use tokio::io::{self, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};\n\n    use crate::stream::{AsyncFsync, AsyncLen, IntoAsyncWriter};\n\n    impl IntoAsyncWriter for Segment {\n        type AsyncWriter = tokio::io::BufWriter<Self>;","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/commitlog/src/repo/mem/segment.rs#L234-L270","documentation":"The memory repo emulates a storage device of fixed capacity: Memory::new(total_space) creates a SpaceOnDevice budget shared by all segments, and segments allocate space in PAGE_SIZE (4096-byte) increments. When a write, ftruncate, or fallocate needs more pages than remain, the allocation returns io::ErrorKind::StorageFull with the POSIX ENOSPC message.","triggerScenarios":"Constructing Memory::new with a small total_space in tests and writing more data than it allows; fallocating Options::max_segment_size for several segments until the budget is exhausted; ftruncate extending a segment beyond available space (see mem.rs tests: ftruncate to 9216 on a 2-page budget fails).","commonSituations":"Tests that intentionally set a tiny device budget to assert backpressure; forgetting that fallocate/preallocation reserves capacity per segment; sizing total_space below max_segment_size times the expected segment count.","solutions":["Raise the total_space passed to Memory::new, or use Memory::unlimited() when capacity simulation is not the point of the test","Size the budget for worst case: at least max_segment_size per live segment plus overhead","Remove or compact old segments to release their pages before creating new ones","Catch ErrorKind::StorageFull and treat it as a backpressure signal rather than a hard failure"],"exampleFix":"// before\nlet repo = Memory::new(4096); // one page only\n\n// after\n// budget for N segments of max_segment_size each\nlet repo = Memory::new(options.max_segment_size.get() * n_segments as u64 + PAGE_SIZE as u64);","handlingStrategy":"try-catch","validationCode":"// If you constructed the Memory repo yourself, track the budget:\n// space is Arc<Mutex<u64>>; needed pages = ceil(bytes / PAGE_SIZE)\nlet pages_needed = (bytes + PAGE_SIZE - 1) / PAGE_SIZE;\nif *space.lock().unwrap() < pages_needed as u64 * PAGE_SIZE as u64 {\n    return Err(io::Error::new(io::ErrorKind::StorageFull, \"insufficient budget\"));\n}","typeGuard":"fn is_storage_full(err: &io::Error) -> bool {\n    err.kind() == io::ErrorKind::StorageFull\n}","tryCatchPattern":"match segment.write_all(&buf) {\n    Ok(()) => Ok(()),\n    Err(e) if is_storage_full(&e) => {\n        // release capacity (remove/compact old segments) or apply backpressure\n        handle_full_device()?;\n        segment.write_all(&buf)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Budget total_space as max_segment_size * max_live_segments + one page of slack","Use Memory::unlimited() in tests that do not specifically test capacity behavior","Treat StorageFull as backpressure: pause writers and free segments instead of crashing"],"tags":["rust","commitlog","in-memory","storage-full","enospc"],"backgroundTag":"no-space-left-on-device","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}