Skip to main content

StorageBackend

Trait StorageBackend 

Source
pub trait StorageBackend:
    Send
    + Sync
    + 'static {
    // Required methods
    fn put_segment(
        &self,
        path: &SegmentPath,
        data: &[u8],
    ) -> impl Future<Output = Result<()>> + Send;
    fn get_segment(
        &self,
        path: &SegmentPath,
    ) -> impl Future<Output = Result<Vec<u8>>> + Send;
    fn get_range(
        &self,
        path: &SegmentPath,
        offset: u64,
        length: usize,
    ) -> impl Future<Output = Result<Vec<u8>>> + Send;
    fn delete_segment(
        &self,
        path: &SegmentPath,
    ) -> impl Future<Output = Result<()>> + Send;
    fn list_segments(
        &self,
        namespace: &NamespaceId,
        shard_id: &ShardId,
    ) -> impl Future<Output = Result<Vec<SegmentPath>>> + Send;
    fn exists(
        &self,
        path: &SegmentPath,
    ) -> impl Future<Output = Result<bool>> + Send;
}
Expand description

Pluggable storage backend for segment files.

Abstracts the physical storage of segment files, enabling local filesystem, S3, and other backends. All operations are async for cloud compatibility.

§Thread Safety

Implementations must be Send + Sync + 'static for use across Tokio tasks.

§Atomicity

put_segment must write atomically (write-to-temp then rename) to prevent partial/corrupt segment files from being visible.

Required Methods§

Source

fn put_segment( &self, path: &SegmentPath, data: &[u8], ) -> impl Future<Output = Result<()>> + Send

Write a complete segment file to storage.

Implementations must write atomically (write-to-temp → rename).

Source

fn get_segment( &self, path: &SegmentPath, ) -> impl Future<Output = Result<Vec<u8>>> + Send

Read a complete segment file from storage.

Source

fn get_range( &self, path: &SegmentPath, offset: u64, length: usize, ) -> impl Future<Output = Result<Vec<u8>>> + Send

Read a byte range from a segment file.

Critical for column projection — reads only the required columns without loading the entire segment. Uses positioned reads (pread) to avoid seek/read race conditions.

§Partial Read Semantics
  • If offset + length exceeds the file size, implementations MUST return an error (not a short read). Callers rely on exact-length results for correct column decoding.
  • offset is 0-based from the start of the segment file.
  • length == 0 returns an empty Vec<u8>.
  • Implementations MUST NOT cache partial reads internally — the segment cache layer handles caching at a higher level.
  • For object-store backends (S3, GCS), this maps to an HTTP Range request: Range: bytes={offset}-{offset+length-1}.
  • Concurrent get_range calls on the same segment MUST be safe (no file-level locking required — use positioned/pread I/O).
Source

fn delete_segment( &self, path: &SegmentPath, ) -> impl Future<Output = Result<()>> + Send

Delete a segment file from storage.

Source

fn list_segments( &self, namespace: &NamespaceId, shard_id: &ShardId, ) -> impl Future<Output = Result<Vec<SegmentPath>>> + Send

List all segment files in a namespace/shard.

Source

fn exists( &self, path: &SegmentPath, ) -> impl Future<Output = Result<bool>> + Send

Check whether a segment file exists.

Dyn Compatibility§

This trait is not dyn compatible.

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

Implementors§