clockworklabs/SpacetimeDB · error · io::Error

StorageFull

StorageFull

Error message

no space left on device

What it means

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.

Source

Thrown at crates/commitlog/src/repo/mem/segment.rs:252

        if *avail == 0 {
            return Err(enospc());
        }

        let want = size.next_multiple_of(PAGE_SIZE as u64) - storage.alloc;
        let have = want.min(*avail);
        storage.alloc += have;
        *avail -= have;

        if want > have {
            return Err(enospc());
        }

        Ok(())
    }
}

fn enospc() -> io::Error {
    io::Error::new(io::ErrorKind::StorageFull, "no space left on device")
}

#[cfg(feature = "streaming")]
mod async_impls {
    use super::*;

    use std::{
        io::{Read as _, Seek as _, Write as _},
        pin::Pin,
        task::{Context, Poll},
    };

    use tokio::io::{self, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};

    use crate::stream::{AsyncFsync, AsyncLen, IntoAsyncWriter};

    impl IntoAsyncWriter for Segment {
        type AsyncWriter = tokio::io::BufWriter<Self>;

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Increase the budget: Memory::new(total_space) sized for max_segment_size times the expected segment count
  2. Free budget by removing/compacting old segments once their data is no longer needed
  3. Catch StorageFull at the producer and pause/shed load instead of crashing

Example fix

// before
let repo = Memory::new(1024 * 1024); // 1 MiB budget, exhausts quickly

// after
let opts = Options::default();
let budget = opts.max_segment_size * 4; // room for 4 full segments
let repo = Memory::new(budget);
Defensive patterns

Strategy: try-catch

Validate before calling

// budget the memory repo for the segments you expect
let opts = Options::default();
let expected_segments = 4;
let repo = Memory::new(opts.max_segment_size * expected_segments + (1 << 20));

Type guard

fn is_storage_full(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::StorageFull
}

Try / catch

match log.commit(batch) {
    Ok(committed) => { /* ... */ }
    Err(e) if is_storage_full(&e) => {
        // shed load: pause producers, compact/remove old segments, then resume
        backpressure_and_compact();
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: Unit/soak tests with a small Memory budget; fuzz setups deliberately simulating out-of-disk conditions.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/44919e148ecc3de1. Report an issue: GitHub.