Skip to main content

BlobStore

Trait BlobStore 

pub trait BlobStore: Send + Sync {
    // Required methods
    fn read_blob(&self, guid: BlobGuid, dst: &mut AlignedBlobBuf) -> Result<()>;
    fn write_blob(&self, guid: BlobGuid, src: &AlignedBlobBuf) -> Result<()>;
    fn delete_blob(&self, guid: BlobGuid) -> Result<()>;
    fn list_blobs(&self) -> Result<Vec<BlobGuid>>;
    fn flush(&self) -> Result<()>;

    // Provided methods
    fn alloc_blob_buf_zeroed(&self) -> AlignedBlobBuf { ... }
    fn alloc_blob_buf_uninit(&self) -> AlignedBlobBuf { ... }
    fn write_blobs(&self, writes: &[(BlobGuid, &AlignedBlobBuf)]) -> Result<()> { ... }
    fn write_blobs_with_data_sync(
        &self,
        writes: &[(BlobGuid, &AlignedBlobBuf)],
    ) -> Result<()> { ... }
    fn needs_flush(&self) -> bool { ... }
    fn has_blob(&self, guid: BlobGuid) -> Result<bool> { ... }
}
Expand description

A blob-granular storage interface.

All implementations are Send + Sync so the buffer manager can drive concurrent I/O from multiple worker threads.

§Contract

  • read_blob / write_blob always operate on a full PAGE_SIZE-byte frame. Partial I/O is not supported.
  • write_blob is atomic at the blob level: either the entire new image is visible to subsequent reads, or the old image is. No torn writes (FileBlobStore achieves this via O_DIRECT page-aligned writes on NVMe — physically atomic for ≤ 4 KB, logically atomic for 512 KB via journal coordination).
  • flush blocks until every write that returned before the call is durable on the underlying medium.

Required Methods§

fn read_blob(&self, guid: BlobGuid, dst: &mut AlignedBlobBuf) -> Result<()>

Read blob guid into dst. dst.len() == PAGE_SIZE.

fn write_blob(&self, guid: BlobGuid, src: &AlignedBlobBuf) -> Result<()>

Write src as blob guid. src.len() == PAGE_SIZE.

Returns once the write has been submitted to the medium. Call BlobStore::flush to wait for it to be durable.

fn delete_blob(&self, guid: BlobGuid) -> Result<()>

Delete blob guid. No-op if it doesn’t exist.

fn list_blobs(&self) -> Result<Vec<BlobGuid>>

Enumerate every blob currently stored.

fn flush(&self) -> Result<()>

Wait until every previously-returned write is durable.

Provided Methods§

fn alloc_blob_buf_zeroed(&self) -> AlignedBlobBuf

Allocate a zero-filled blob buffer suitable for this store.

The default is a heap-backed 4 KB-aligned frame. Linux io_uring file stores override this to lease from their registered fixed-buffer pool when available.

fn alloc_blob_buf_uninit(&self) -> AlignedBlobBuf

Allocate an uninitialized blob buffer suitable for this store. Callers must fill all bytes before reading.

fn write_blobs(&self, writes: &[(BlobGuid, &AlignedBlobBuf)]) -> Result<()>

Write a batch of full-blob images.

The default implementation loops over Self::write_blob. Stores with a cheaper native batch path should override this. The contract is conservative: if this returns Err, the caller must assume an arbitrary prefix may have reached the store and retry the whole batch later.

fn write_blobs_with_data_sync( &self, writes: &[(BlobGuid, &AlignedBlobBuf)], ) -> Result<()>

Write a batch and, if the store can do it cheaply, make the data-file bytes durable before returning.

This is deliberately narrower than Self::flush: callers must still call flush to persist metadata/manifest changes. The hook exists for Linux io_uring, where checkpoint write batches can keep data writes and fdatasync on the same ring turn, then let the later manifest flush skip the data sync if no newer writes raced in.

fn needs_flush(&self) -> bool

Conservative hint for callers that want to skip a no-op flush. Stores should return true whenever a prior returned write, delete, or metadata update still needs Self::flush to make it durable.

fn has_blob(&self, guid: BlobGuid) -> Result<bool>

true iff guid exists. Default impl scans list_blobs.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§