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§
Sourcefn put_segment(
&self,
path: &SegmentPath,
data: &[u8],
) -> impl Future<Output = Result<()>> + Send
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).
Sourcefn get_segment(
&self,
path: &SegmentPath,
) -> impl Future<Output = Result<Vec<u8>>> + Send
fn get_segment( &self, path: &SegmentPath, ) -> impl Future<Output = Result<Vec<u8>>> + Send
Read a complete segment file from storage.
Sourcefn get_range(
&self,
path: &SegmentPath,
offset: u64,
length: usize,
) -> impl Future<Output = Result<Vec<u8>>> + Send
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 + lengthexceeds the file size, implementations MUST return an error (not a short read). Callers rely on exact-length results for correct column decoding. offsetis 0-based from the start of the segment file.length == 0returns an emptyVec<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_rangecalls on the same segment MUST be safe (no file-level locking required — use positioned/pread I/O).
Sourcefn delete_segment(
&self,
path: &SegmentPath,
) -> impl Future<Output = Result<()>> + Send
fn delete_segment( &self, path: &SegmentPath, ) -> impl Future<Output = Result<()>> + Send
Delete a segment file from storage.
Sourcefn list_segments(
&self,
namespace: &NamespaceId,
shard_id: &ShardId,
) -> impl Future<Output = Result<Vec<SegmentPath>>> + Send
fn list_segments( &self, namespace: &NamespaceId, shard_id: &ShardId, ) -> impl Future<Output = Result<Vec<SegmentPath>>> + Send
List all segment files in a namespace/shard.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".