pub struct TieredStorage { /* private fields */ }Expand description
Two-tier storage backend that routes objects by size.
TieredStorage implements Backend and is intended to be used inside a
StorageService, which wraps it with task spawning and panic
isolation.
§Size-Based Routing
Objects are routed at write time based on their size relative to a 1 MiB threshold:
- Objects ≤ 1 MiB go to the
high_volumebackend — optimized for low-latency reads and writes of small objects (e.g. BigTable). - Objects > 1 MiB go to the
long_termbackend — optimized for cost-efficient storage of large objects (e.g. GCS).
§Redirect Tombstones
Because the ObjectId is backend-independent, reads must be able to find an object
without knowing which backend stores it. A naive approach would check the long-term
backend on every read miss in the high-volume backend — but that is slow and expensive.
Instead, when an object is stored in the long-term backend, a redirect tombstone is written in the high-volume backend. It acts as a signpost: “the real data lives in the other backend at this target.” On reads, a single high-volume lookup either returns the object directly or follows the tombstone to long-term storage, without probing both backends.
How tombstones are physically stored is determined by the HighVolumeBackend
implementation — refer to the backend’s own documentation for storage format details.
§Consistency
Consistency across the two backends is maintained through compare-and-swap
operations on the high-volume backend (see
HighVolumeBackend::compare_and_write), not distributed locks. Each
mutating operation reads the current high-volume revision, performs its
work, and then atomically swaps the high-volume entry only if the revision
is still current — rolling back on conflict. Cleanup of unreferenced LT
blobs runs in background tasks so the caller returns as soon as the commit
point is reached. Call Backend::join during shutdown to wait for
outstanding cleanup.
See the module-level documentation for per-operation diagrams.
§Usage
TieredStorage handles only the routing and consistency logic. Wrap it in a
StorageService to add task spawning, panic isolation,
and concurrency limiting.
Implementations§
Trait Implementations§
Source§impl Backend for TieredStorage
impl Backend for TieredStorage
Source§fn put_object<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
metadata: &'life2 Metadata,
stream: ClientStream,
) -> Pin<Box<dyn Future<Output = Result<PutResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn put_object<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
metadata: &'life2 Metadata,
stream: ClientStream,
) -> Pin<Box<dyn Future<Output = Result<PutResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Source§fn get_object<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
) -> Pin<Box<dyn Future<Output = Result<GetResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_object<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
) -> Pin<Box<dyn Future<Output = Result<GetResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_metadata<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
) -> Pin<Box<dyn Future<Output = Result<MetadataResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_metadata<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
) -> Pin<Box<dyn Future<Output = Result<MetadataResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn delete_object<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
) -> Pin<Box<dyn Future<Output = Result<DeleteResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn delete_object<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 ObjectId,
) -> Pin<Box<dyn Future<Output = Result<DeleteResponse>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Auto Trait Implementations§
impl Freeze for TieredStorage
impl !RefUnwindSafe for TieredStorage
impl Send for TieredStorage
impl Sync for TieredStorage
impl Unpin for TieredStorage
impl UnsafeUnpin for TieredStorage
impl !UnwindSafe for TieredStorage
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].