{"record":{"id":"44919e148ecc3de1","repo":"clockworklabs/SpacetimeDB","slug":"storagefull","errorCode":"StorageFull","errorMessage":"no space left on device","messagePattern":"no space left on device","errorType":"exception","errorClass":"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/524b4487d949b61a07d4f39c862d1290259dfd20/crates/commitlog/src/repo/mem/segment.rs#L234-L270","documentation":"The in-memory repo simulates a disk with a fixed budget (Memory::new(total_space)); segments allocate space from that budget, and when a write needs more than remains you get ErrorKind::StorageFull with the classic ENOSPC message. This is the memory backend's disk-full simulation, not a real filesystem condition.","triggerScenarios":"Committing until total allocated segment space exceeds the total_space passed to Memory::new; a single large commit arriving when the budget is nearly exhausted; tests that never remove old segments.","commonSituations":"Unit/soak tests with a small Memory budget; fuzz setups deliberately simulating out-of-disk conditions.","solutions":["Increase the budget: Memory::new(total_space) sized for max_segment_size times the expected segment count","Free budget by removing/compacting old segments once their data is no longer needed","Catch StorageFull at the producer and pause/shed load instead of crashing"],"exampleFix":"// before\nlet repo = Memory::new(1024 * 1024); // 1 MiB budget, exhausts quickly\n\n// after\nlet opts = Options::default();\nlet budget = opts.max_segment_size * 4; // room for 4 full segments\nlet repo = Memory::new(budget);","handlingStrategy":"try-catch","validationCode":"// budget the memory repo for the segments you expect\nlet opts = Options::default();\nlet expected_segments = 4;\nlet repo = Memory::new(opts.max_segment_size * expected_segments + (1 << 20));","typeGuard":"fn is_storage_full(e: &io::Error) -> bool {\n    e.kind() == io::ErrorKind::StorageFull\n}","tryCatchPattern":"match log.commit(batch) {\n    Ok(committed) => { /* ... */ }\n    Err(e) if is_storage_full(&e) => {\n        // shed load: pause producers, compact/remove old segments, then resume\n        backpressure_and_compact();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Size Memory::new(total_space) for max_segment_size times the maximum segment count","Remove or compact segments once their data is durably consumed elsewhere","Treat StorageFull as backpressure, never as a crash"],"tags":["rust","commitlog","memory-repo","storage-full","enospc","capacity"],"backgroundTag":"storage-capacity-exceeded","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}