pub struct SequentialWritePolicy;Expand description
Sequential write policy: Write to L1, then L2 (write-through).
This is the default and most common strategy. It provides:
- Strong consistency (both layers updated before returning)
- Atomic updates from caller’s perspective
- Graceful degradation if L1 succeeds but L2 fails
§Behavior
- Call
write_l1(key)- If fails: Return error immediately (don’t write to L2)
- If succeeds: Continue to L2
- Call
write_l2(key)- If fails: Return error (L1 has data, L2 doesn’t - inconsistent state)
- If succeeds: Return success
§Consistency Guarantees
Success case (Ok(())): Both L1 and L2 have been updated successfully.
Failure cases (Err):
- L1 write failed: Neither layer updated - cache remains consistent
- L2 write failed: L1 updated, L2 not updated - inconsistent state
§Inconsistent State Handling
When L1 succeeds but L2 fails, the cache enters an inconsistent state where:
- L1 contains the new value - subsequent reads from this client will hit L1
- L2 may contain stale data or no data - other clients may see stale values
- The error is logged with tracing::error for monitoring
§Mitigation Strategies:
-
Accept inconsistency - If L1 is much faster and L2 failures are rare, the inconsistency may be acceptable as L1 will mask it for most reads
-
Retry logic - Implement retry at application level or use a RetryBackend wrapper to retry failed L2 writes
-
Use OptimisticParallelWritePolicy - Succeeds if either L1 or L2 succeeds, providing better availability at the cost of potential inconsistency
-
Monitor and alert - Track L2 write failures via metrics and investigate persistent failures that could indicate L2 capacity or connectivity issues
§When L2 Failures Are Acceptable:
- L2 is a persistent cache for cold starts (L1 mask inconsistency during normal operation)
- Cache data is regeneratable from source of truth
- Read-heavy workload where L1 hit rate is very high
Implementations§
Trait Implementations§
Source§impl Clone for SequentialWritePolicy
impl Clone for SequentialWritePolicy
Source§fn clone(&self) -> SequentialWritePolicy
fn clone(&self) -> SequentialWritePolicy
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl CompositionWritePolicy for SequentialWritePolicy
impl CompositionWritePolicy for SequentialWritePolicy
Source§fn execute_with<'life0, 'life1, 'async_trait, F1, F2, Fut1, Fut2, O>(
&'life0 self,
key: CacheKey,
write_l1: F1,
write_l2: F2,
_offload: &'life1 O,
) -> Pin<Box<dyn Future<Output = Result<(), BackendError>> + Send + 'async_trait>>where
F1: FnOnce(CacheKey) -> Fut1 + Send + 'async_trait,
F2: FnOnce(CacheKey) -> Fut2 + Send + 'async_trait,
Fut1: Future<Output = Result<(), BackendError>> + Send + 'static + 'async_trait,
Fut2: Future<Output = Result<(), BackendError>> + Send + 'static + 'async_trait,
O: Offload<'static> + 'async_trait,
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn execute_with<'life0, 'life1, 'async_trait, F1, F2, Fut1, Fut2, O>(
&'life0 self,
key: CacheKey,
write_l1: F1,
write_l2: F2,
_offload: &'life1 O,
) -> Pin<Box<dyn Future<Output = Result<(), BackendError>> + Send + 'async_trait>>where
F1: FnOnce(CacheKey) -> Fut1 + Send + 'async_trait,
F2: FnOnce(CacheKey) -> Fut2 + Send + 'async_trait,
Fut1: Future<Output = Result<(), BackendError>> + Send + 'static + 'async_trait,
Fut2: Future<Output = Result<(), BackendError>> + Send + 'static + 'async_trait,
O: Offload<'static> + 'async_trait,
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§impl Debug for SequentialWritePolicy
impl Debug for SequentialWritePolicy
Source§impl Default for SequentialWritePolicy
impl Default for SequentialWritePolicy
Source§fn default() -> SequentialWritePolicy
fn default() -> SequentialWritePolicy
impl Copy for SequentialWritePolicy
Auto Trait Implementations§
impl Freeze for SequentialWritePolicy
impl RefUnwindSafe for SequentialWritePolicy
impl Send for SequentialWritePolicy
impl Sync for SequentialWritePolicy
impl Unpin for SequentialWritePolicy
impl UnwindSafe for SequentialWritePolicy
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.