pub struct SnapshotSource<L>where
L: SnapshotLoader,{ /* private fields */ }Expand description
The whole-table snapshot cache described in the module doc comment, generic over a
SnapshotLoader.
Implementations§
Source§impl<L> SnapshotSource<L>where
L: SnapshotLoader,
impl<L> SnapshotSource<L>where
L: SnapshotLoader,
Sourcepub fn new(pool: Pool<Postgres>, ttl: Duration) -> SnapshotSource<L>
pub fn new(pool: Pool<Postgres>, ttl: Duration) -> SnapshotSource<L>
Builds a source over pool (expected to be a crate::db_api_key::dedicated_key_store_pool,
not the caller’s lake pool directly), with no snapshot loaded yet – the first current()
call performs the first query.
Sourcepub async fn current(
&self,
) -> Result<Arc<<L as SnapshotLoader>::Snapshot>, Error>
pub async fn current( &self, ) -> Result<Arc<<L as SnapshotLoader>::Snapshot>, Error>
Returns the current snapshot, refreshing it first if stale.
Both the cold-start path (no Snapshot yet) and the post-success path are throttled to at
most one DB query per TTL window: cold-start via last_attempt_at (checked-and-set the
same compare-exchange way db_api_key.rs::maybe_log_error rate-limits its own log line),
the post-success path via Snapshot::fetched_at as before. A current() call that lands
inside an already-throttled window returns the last snapshot if one exists; if not, it
returns a freshly synthesized error describing the throttled cold-start state rather than
storing and re-handing-out the prior attempt’s anyhow::Error. A concurrent caller that
loses the last_attempt_at compare-exchange while the very first cold-start attempt is
still in flight re-checks self.snapshot once more before giving up: if the winning
caller has since populated it, the loser serves that snapshot instead of failing; only if
it is still empty does it get the synthesized throttled-state error. It never blocks on
another caller’s still-in-flight query, and it never skips the query silently with nothing
to show for it.
Err only when there has never been one successful load – a fresh process whose first
query hits a down DB has no “last good” to serve, so it fails closed like everything else
on this seam, at a rate capped by last_attempt_at rather than once per request. Once any
load has succeeded, a later refresh failure is logged + counted (via
SnapshotLoader::count_refresh_error) and the last good snapshot keeps serving,
unbounded – this store has no per-item TTL eviction to fall back on the way
db_api_key.rs’s cache does, so an outage degrades to staleness for as long as it lasts,
not just one TTL window.
Trait Implementations§
Source§impl<L> Debug for SnapshotSource<L>where
L: SnapshotLoader,
impl<L> Debug for SnapshotSource<L>where
L: SnapshotLoader,
Source§impl GroupSnapshot for SnapshotSource<GroupsLoader>
impl GroupSnapshot for SnapshotSource<GroupsLoader>
Source§fn current<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Arc<GroupGraph>, Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
SnapshotSource<GroupsLoader>: 'async_trait,
fn current<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Arc<GroupGraph>, Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
SnapshotSource<GroupsLoader>: 'async_trait,
GroupGraph snapshot, or Err (typically a
crate::types::ProviderUnavailable) on a store outage with no prior successful load.Auto Trait Implementations§
impl<L> !Freeze for SnapshotSource<L>
impl<L> !RefUnwindSafe for SnapshotSource<L>
impl<L> !UnwindSafe for SnapshotSource<L>
impl<L> Send for SnapshotSource<L>
impl<L> Sync for SnapshotSource<L>
impl<L> Unpin for SnapshotSource<L>
impl<L> UnsafeUnpin for SnapshotSource<L>
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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§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].